From 27f97044032c1a841fe0644046a81599d598759c Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 23 Oct 2024 14:08:32 -0400 Subject: [PATCH 001/451] chore: Remove unused script (#12597) It has been replaced with a go script. https://github.com/ethereum-optimism/optimism/blob/rm/check-ifaces-sh/packages/contracts-bedrock/scripts/checks/interfaces/main.go#L1 --- .../scripts/checks/check-interfaces.sh | 286 ------------------ 1 file changed, 286 deletions(-) delete mode 100755 packages/contracts-bedrock/scripts/checks/check-interfaces.sh diff --git a/packages/contracts-bedrock/scripts/checks/check-interfaces.sh b/packages/contracts-bedrock/scripts/checks/check-interfaces.sh deleted file mode 100755 index a2cda470d2a8..000000000000 --- a/packages/contracts-bedrock/scripts/checks/check-interfaces.sh +++ /dev/null @@ -1,286 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Warn users of Mac OSX who have not ever upgraded bash from the default that they may experience -# performance issues. -if [ "${BASH_VERSINFO[0]}" -lt 5 ]; then - echo "WARNING: your bash installation is very old, and may cause this script to run extremely slowly. Please upgrade bash to at least version 5 if you have performance issues." -fi - -# This script checks for ABI consistency between interfaces and their corresponding contracts. -# It compares the ABIs of interfaces (files starting with 'I') with their implementation contracts, -# excluding certain predefined files. Constructors are expected to be represented in interfaces by a -# pseudo-constructor function `__constructor__(...)` with arguments the same as the contract's constructor. -# The script reports any differences found and exits with an error if inconsistencies are detected. -# NOTE: Script is fast enough but could be parallelized if necessary. - -# Parse flags -no_diff=false -if [[ "${1:-}" == "--no-diff" ]]; then - no_diff=true -fi - -# Grab the directory of the contracts-bedrock package -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -CONTRACTS_BASE=$(dirname "$(dirname "$SCRIPT_DIR")") - -# Define contracts that should be excluded from the check -EXCLUDE_CONTRACTS=( - # External dependencies - "IERC20" - "IERC721" - "IERC721Enumerable" - "IERC721Upgradeable" - "IERC721Metadata" - "IERC165" - "IERC165Upgradeable" - "ERC721TokenReceiver" - "ERC1155TokenReceiver" - "ERC777TokensRecipient" - "Guard" - "IProxy" - "Vm" - "VmSafe" - "IMulticall3" - "IERC721TokenReceiver" - "IProxyCreationCallback" - "IBeacon" - - # EAS - "IEAS" - "ISchemaResolver" - "ISchemaRegistry" - - # TODO: Interfaces that need to be fixed are below this line - # ---------------------------------------------------------- - - # Inlined interface, needs to be replaced. - "IInitializable" - - # Missing various functions. - "IPreimageOracle" - "ILegacyMintableERC20" - "IOptimismMintableERC20" - "IOptimismMintableERC721" - - # Doesn't start with "I" - "KontrolCheatsBase" - - # Currently inherit from interface, needs to be fixed. - "IWETH" - "IDelayedWETH" - "ISuperchainWETH" - "IL2ToL2CrossDomainMessenger" - "ICrossL2Inbox" - "ISystemConfigInterop" - - # Enums need to be normalized - "ISequencerFeeVault" - "IBaseFeeVault" - "IL1FeeVault" - "IFeeVault" - - # Solidity complains about receive but contract doens't have it. - "IResolvedDelegateProxy" -) - -# Find all JSON files in the forge-artifacts folder -JSON_FILES=$(find "$CONTRACTS_BASE/forge-artifacts" -type f -name "*.json") - -# Initialize a flag to track if any issues are detected -issues_detected=false - -# Create a temporary file to store files that have already been reported -REPORTED_INTERFACES_FILE=$(mktemp) - -# Clean up the temporary file on exit -cleanup() { - rm -f "$REPORTED_INTERFACES_FILE" -} - -# Trap exit and error signals and call cleanup function -trap cleanup EXIT ERR - -# Check if a contract is excluded -is_excluded() { - for exclude in "${EXCLUDE_CONTRACTS[@]}"; do - if [[ "$exclude" == "$1" ]]; then - return 0 - fi - done - return 1 -} - -# Iterate over all JSON files -for interface_file in $JSON_FILES; do - # Grab the contract name from the file name - contract_name=$(basename "$interface_file" .json | cut -d '.' -f 1) - - # Extract all contract definitions in a single pass - contract_definitions=$(jq -r '.ast.nodes[] | select(.nodeType == "ContractDefinition") | "\(.contractKind),\(.name)"' "$interface_file") - - # Continue if no contract definitions are found - # Can happen in Solidity files that don't declare any contracts/interfaces - if [ -z "$contract_definitions" ]; then - continue - fi - - # Iterate over the found contract definitions and figure out which one - # matches the file name. We do this so that we can figure out if this is an - # interface or a contract based on the contract kind. - found=false - contract_temp="" - contract_kind="" - for definition in $contract_definitions; do - IFS=',' read -r contract_kind contract_temp <<< "$definition" - if [[ "$contract_name" == "$contract_temp" ]]; then - found=true - break - fi - done - - # Continue if no matching contract name is found. Can happen in Solidity - # files where no contracts/interfaces are defined with the same name as the - # file. Still OK because a separate artifact *will* be generated for the - # specific contract/interface. - if [ "$found" = false ]; then - continue - fi - - # If contract kind is not "interface", skip the file - if [ "$contract_kind" != "interface" ]; then - continue - fi - - # If contract name does not start with an "I", throw an error - if [[ "$contract_name" != I* ]]; then - if ! grep -q "^$contract_name$" "$REPORTED_INTERFACES_FILE"; then - echo "$contract_name" >> "$REPORTED_INTERFACES_FILE" - if ! is_excluded "$contract_name"; then - echo "Issue found in ABI for interface $contract_name from file $interface_file." - echo "Interface $contract_name does not start with 'I'." - issues_detected=true - fi - fi - continue - fi - - # Extract contract semver - contract_semver=$(jq -r '.ast.nodes[] | select(.nodeType == "PragmaDirective") | .literals | join("")' "$interface_file") - - # If semver is not exactly "solidity^0.8.0", throw an error - if [ "$contract_semver" != "solidity^0.8.0" ]; then - if ! grep -q "^$contract_name$" "$REPORTED_INTERFACES_FILE"; then - echo "$contract_name" >> "$REPORTED_INTERFACES_FILE" - if ! is_excluded "$contract_name"; then - echo "Issue found in ABI for interface $contract_name from file $interface_file." - echo "Interface $contract_name does not have correct compiler version (MUST be exactly solidity ^0.8.0)." - issues_detected=true - fi - fi - continue - fi - - # Construct the corresponding contract name by removing the leading "I" - contract_basename=${contract_name:1} - corresponding_contract_file="$CONTRACTS_BASE/forge-artifacts/$contract_basename.sol/$contract_basename.json" - - # Skip the file if the corresponding contract file does not exist - if [ ! -f "$corresponding_contract_file" ]; then - continue - fi - - # Extract and compare ABIs excluding constructors - interface_abi=$(jq '[.abi[]]' < "$interface_file") - contract_abi=$(jq '[.abi[]]' < "$corresponding_contract_file") - - # Function to normalize ABI by replacing interface name with contract name. - # Base contracts aren't allowed to inherit from their interfaces in order - # to guarantee a 1:1 match between interfaces and contracts. This means - # that the interface will redefine types in the base contract. We normalize - # the ABI as if the interface and contract are the same name. - normalize_abi() { - # Here we just remove the leading "I" from any contract, enum, or - # struct type. It's not beautiful but it's good enough for now. It - # would miss certain edge cases like if an interface really is using - # the contract type instead of the interface type but that's unlikely - # to happen in practice and should be an easy fix if it does. - local abi="$1" - - # Remove the leading "I" from types. - abi="${abi//\"internalType\": \"contract I/\"internalType\": \"contract }" - abi="${abi//\"internalType\": \"enum I/\"internalType\": \"enum }" - abi="${abi//\"internalType\": \"struct I/\"internalType\": \"struct }" - - # Handle translating pseudo-constructors. - abi=$(echo "$abi" | jq 'map(if .type == "function" and .name == "__constructor__" then .type = "constructor" | del(.name) | del(.outputs) else . end)') - - echo "$abi" - } - - # Normalize the ABIs - normalized_interface_abi=$(normalize_abi "$interface_abi") - normalized_contract_abi=$(normalize_abi "$contract_abi") - - # Check if the contract ABI has no constructor but the interface is missing __constructor__ - contract_has_constructor=$(echo "$normalized_contract_abi" | jq 'any(.[]; .type == "constructor")') - interface_has_default_pseudo_constructor=$(echo "$normalized_interface_abi" | jq 'any(.[]; .type == "constructor" and .inputs == [])') - - # If any contract has no constructor and its corresponding interface also does not have one, flag it as a detected issue - if [ "$contract_has_constructor" = false ] && [ "$interface_has_default_pseudo_constructor" = false ]; then - if ! grep -q "^$contract_name$" "$REPORTED_INTERFACES_FILE"; then - echo "$contract_name" >> "$REPORTED_INTERFACES_FILE" - if ! is_excluded "$contract_name"; then - echo "Issue found in ABI for interface $contract_name from file $interface_file." - echo "Interface $contract_name must have a function named '__constructor__' as the corresponding contract has no constructor in its ABI." - issues_detected=true - fi - fi - continue - fi - - # removes the pseudo constructor json entry from the interface files where the corresponding contract file has no constructor - # this is to ensure it is not flagged as a diff in the next step below - if [ "$contract_has_constructor" = false ] && [ "$interface_has_default_pseudo_constructor" ]; then - normalized_interface_abi=$(echo "$normalized_interface_abi" | jq 'map(select(.type != "constructor"))') - fi - - # Use jq to compare the ABIs - if ! diff_result=$(diff -u <(echo "$normalized_interface_abi" | jq 'sort') <(echo "$normalized_contract_abi" | jq 'sort')); then - if ! grep -q "^$contract_name$" "$REPORTED_INTERFACES_FILE"; then - echo "$contract_name" >> "$REPORTED_INTERFACES_FILE" - if ! is_excluded "$contract_name"; then - echo "Issue found in ABI for interface $contract_name from file $interface_file." - echo "Differences found in ABI between interface $contract_name and actual contract $contract_basename." - if [ "$no_diff" = false ]; then - echo "$diff_result" - fi - issues_detected=true - fi - fi - continue - fi -done - -# Check for unnecessary exclusions -for exclude_item in "${EXCLUDE_CONTRACTS[@]}"; do - if ! grep -q "^$exclude_item$" "$REPORTED_INTERFACES_FILE"; then - echo "Warning: $exclude_item is in the exclude list but WAS NOT reported as an issue. It" - echo "may be unnecessary in the EXCLUDE_CONTRACTS list, but you MUST verify this before" - echo "removing it by performing a clean and full build before re-running this script." - fi -done - -# Fail the script if any issues were detected -if [ "$issues_detected" = true ]; then - echo "Issues were detected while validating interface files." - echo "If the interface is an external dependency or should otherwise be excluded from this" - echo "check, add the interface name to the EXCLUDE_CONTRACTS list in the script. This will prevent" - echo "the script from comparing it against a corresponding contract." - echo "IMPORTANT: Interface files are NOT yet generated automatically. You must fix any" - echo "listed discrepancies manually by updating the specified interface file. Automated" - echo "interface generation is dependent on a few Forge bug fixes." - exit 1 -else - exit 0 -fi From d67991a3fc4f0a755cb507a33bca0b6c111c83fd Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Thu, 24 Oct 2024 04:23:18 +1000 Subject: [PATCH 002/451] op-dispute-mon: Use game data from previous update cycle if update fails (#12481) --- op-dispute-mon/metrics/metrics.go | 15 +++- op-dispute-mon/metrics/noop.go | 2 + op-dispute-mon/mon/extract/extractor.go | 22 ++++-- op-dispute-mon/mon/extract/extractor_test.go | 78 ++++++++++++++++---- op-dispute-mon/mon/monitor.go | 35 +++------ op-dispute-mon/mon/monitor_test.go | 75 +++++-------------- op-dispute-mon/mon/service.go | 17 ++--- op-dispute-mon/mon/types/types.go | 1 + op-dispute-mon/mon/update_times.go | 34 +++++++++ op-dispute-mon/mon/update_times_test.go | 43 +++++++++++ 10 files changed, 208 insertions(+), 114 deletions(-) create mode 100644 op-dispute-mon/mon/update_times.go create mode 100644 op-dispute-mon/mon/update_times_test.go diff --git a/op-dispute-mon/metrics/metrics.go b/op-dispute-mon/metrics/metrics.go index b76c23c4ca26..aa8cd9947905 100644 --- a/op-dispute-mon/metrics/metrics.go +++ b/op-dispute-mon/metrics/metrics.go @@ -183,6 +183,8 @@ type Metricer interface { RecordL2Challenges(agreement bool, count int) + RecordOldestGameUpdateTime(t time.Time) + caching.Metrics contractMetrics.ContractMetricer } @@ -215,7 +217,8 @@ type Metrics struct { credits prometheus.GaugeVec honestWithdrawableAmounts prometheus.GaugeVec - lastOutputFetch prometheus.Gauge + lastOutputFetch prometheus.Gauge + oldestGameUpdateTime prometheus.Gauge gamesAgreement prometheus.GaugeVec latestValidProposalL2Block prometheus.Gauge @@ -269,6 +272,12 @@ func NewMetrics() *Metrics { Name: "last_output_fetch", Help: "Timestamp of the last output fetch", }), + oldestGameUpdateTime: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: Namespace, + Name: "oldest_game_update_time", + Help: "Timestamp the least recently updated game " + + "or the time of the last update cycle if there were no games in the monitoring window", + }), honestActorClaims: *factory.NewGaugeVec(prometheus.GaugeOpts{ Namespace: Namespace, Name: "honest_actor_claims", @@ -499,6 +508,10 @@ func (m *Metrics) RecordOutputFetchTime(timestamp float64) { m.lastOutputFetch.Set(timestamp) } +func (m *Metrics) RecordOldestGameUpdateTime(t time.Time) { + m.oldestGameUpdateTime.Set(float64(t.Unix())) +} + func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) { m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count)) } diff --git a/op-dispute-mon/metrics/noop.go b/op-dispute-mon/metrics/noop.go index 6b4982ff26ba..4459fdd9c1fb 100644 --- a/op-dispute-mon/metrics/noop.go +++ b/op-dispute-mon/metrics/noop.go @@ -36,6 +36,8 @@ func (*NoopMetricsImpl) RecordWithdrawalRequests(_ common.Address, _ bool, _ int func (*NoopMetricsImpl) RecordOutputFetchTime(_ float64) {} +func (*NoopMetricsImpl) RecordOldestGameUpdateTime(_ time.Time) {} + func (*NoopMetricsImpl) RecordGameAgreement(_ GameAgreementStatus, _ int) {} func (*NoopMetricsImpl) RecordLatestValidProposalL2Block(_ uint64) {} diff --git a/op-dispute-mon/mon/extract/extractor.go b/op-dispute-mon/mon/extract/extractor.go index d19cab340b66..c40b31ccb6c5 100644 --- a/op-dispute-mon/mon/extract/extractor.go +++ b/op-dispute-mon/mon/extract/extractor.go @@ -9,9 +9,11 @@ import ( gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/maps" ) var ( @@ -29,20 +31,23 @@ type Enricher interface { type Extractor struct { logger log.Logger + clock clock.Clock createContract CreateGameCaller fetchGames FactoryGameFetcher maxConcurrency int enrichers []Enricher ignoredGames map[common.Address]bool + latestGameData map[common.Address]*monTypes.EnrichedGameData } -func NewExtractor(logger log.Logger, creator CreateGameCaller, fetchGames FactoryGameFetcher, ignoredGames []common.Address, maxConcurrency uint, enrichers ...Enricher) *Extractor { +func NewExtractor(logger log.Logger, cl clock.Clock, creator CreateGameCaller, fetchGames FactoryGameFetcher, ignoredGames []common.Address, maxConcurrency uint, enrichers ...Enricher) *Extractor { ignored := make(map[common.Address]bool) for _, game := range ignoredGames { ignored[game] = true } return &Extractor{ logger: logger, + clock: cl, createContract: creator, fetchGames: fetchGames, maxConcurrency: int(maxConcurrency), @@ -61,7 +66,6 @@ func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimes } func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) ([]*monTypes.EnrichedGameData, int, int) { - var enrichedGames []*monTypes.EnrichedGameData var ignored atomic.Int32 var failed atomic.Int32 @@ -101,8 +105,14 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game }() } - // Push each game into the channel + // Create a new store for game data. This ensures any games no longer in the monitoring set are dropped. + updatedGameData := make(map[common.Address]*monTypes.EnrichedGameData) + // Push each game into the channel and store the latest cached game data as a default if fetching fails for _, game := range games { + previousData := e.latestGameData[game.Proxy] + if previousData != nil { + updatedGameData[game.Proxy] = previousData + } gameCh <- game } close(gameCh) @@ -112,9 +122,10 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game // Read the results for enrichedGame := range enrichedCh { - enrichedGames = append(enrichedGames, enrichedGame) + updatedGameData[enrichedGame.Proxy] = enrichedGame } - return enrichedGames, int(ignored.Load()), int(failed.Load()) + e.latestGameData = updatedGameData + return maps.Values(updatedGameData), int(ignored.Load()), int(failed.Load()) } func (e *Extractor) enrichGame(ctx context.Context, blockHash common.Hash, game gameTypes.GameMetadata) (*monTypes.EnrichedGameData, error) { @@ -138,6 +149,7 @@ func (e *Extractor) enrichGame(ctx context.Context, blockHash common.Hash, game enrichedClaims[i] = monTypes.EnrichedClaim{Claim: claim} } enrichedGame := &monTypes.EnrichedGameData{ + LastUpdateTime: e.clock.Now(), GameMetadata: game, L1Head: meta.L1Head, L2BlockNumber: meta.L2BlockNum, diff --git a/op-dispute-mon/mon/extract/extractor_test.go b/op-dispute-mon/mon/extract/extractor_test.go index 361aeb57420e..ee4b8a63ee25 100644 --- a/op-dispute-mon/mon/extract/extractor_test.go +++ b/op-dispute-mon/mon/extract/extractor_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/stretchr/testify/require" @@ -26,7 +27,7 @@ var ( func TestExtractor_Extract(t *testing.T) { t.Run("FetchGamesError", func(t *testing.T) { - extractor, _, games, _ := setupExtractorTest(t) + extractor, _, games, _, _ := setupExtractorTest(t) games.err = errors.New("boom") _, _, _, err := extractor.Extract(context.Background(), common.Hash{}, 0) require.ErrorIs(t, err, games.err) @@ -34,7 +35,7 @@ func TestExtractor_Extract(t *testing.T) { }) t.Run("CreateGameErrorLog", func(t *testing.T) { - extractor, creator, games, logs := setupExtractorTest(t) + extractor, creator, games, logs, _ := setupExtractorTest(t) games.games = []gameTypes.GameMetadata{{}} creator.err = errors.New("boom") enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) @@ -50,7 +51,7 @@ func TestExtractor_Extract(t *testing.T) { }) t.Run("MetadataFetchErrorLog", func(t *testing.T) { - extractor, creator, games, logs := setupExtractorTest(t) + extractor, creator, games, logs, _ := setupExtractorTest(t) games.games = []gameTypes.GameMetadata{{}} creator.caller.metadataErr = errors.New("boom") enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) @@ -66,7 +67,7 @@ func TestExtractor_Extract(t *testing.T) { }) t.Run("ClaimsFetchErrorLog", func(t *testing.T) { - extractor, creator, games, logs := setupExtractorTest(t) + extractor, creator, games, logs, _ := setupExtractorTest(t) games.games = []gameTypes.GameMetadata{{}} creator.caller.claimsErr = errors.New("boom") enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) @@ -82,7 +83,7 @@ func TestExtractor_Extract(t *testing.T) { }) t.Run("Success", func(t *testing.T) { - extractor, creator, games, _ := setupExtractorTest(t) + extractor, creator, games, _, _ := setupExtractorTest(t) games.games = []gameTypes.GameMetadata{{}} enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) require.NoError(t, err) @@ -97,7 +98,7 @@ func TestExtractor_Extract(t *testing.T) { t.Run("EnricherFails", func(t *testing.T) { enricher := &mockEnricher{err: errors.New("whoops")} - extractor, _, games, logs := setupExtractorTest(t, enricher) + extractor, _, games, logs, _ := setupExtractorTest(t, enricher) games.games = []gameTypes.GameMetadata{{}} enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) require.NoError(t, err) @@ -110,7 +111,7 @@ func TestExtractor_Extract(t *testing.T) { t.Run("EnricherSuccess", func(t *testing.T) { enricher := &mockEnricher{} - extractor, _, games, _ := setupExtractorTest(t, enricher) + extractor, _, games, _, _ := setupExtractorTest(t, enricher) games.games = []gameTypes.GameMetadata{{}} enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) require.NoError(t, err) @@ -123,8 +124,8 @@ func TestExtractor_Extract(t *testing.T) { t.Run("MultipleEnrichersMultipleGames", func(t *testing.T) { enricher1 := &mockEnricher{} enricher2 := &mockEnricher{} - extractor, _, games, _ := setupExtractorTest(t, enricher1, enricher2) - games.games = []gameTypes.GameMetadata{{}, {}} + extractor, _, games, _, _ := setupExtractorTest(t, enricher1, enricher2) + games.games = []gameTypes.GameMetadata{{Proxy: common.Address{0xaa}}, {Proxy: common.Address{0xbb}}} enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) require.NoError(t, err) require.Zero(t, ignored) @@ -136,7 +137,7 @@ func TestExtractor_Extract(t *testing.T) { t.Run("IgnoreGames", func(t *testing.T) { enricher1 := &mockEnricher{} - extractor, _, games, logs := setupExtractorTest(t, enricher1) + extractor, _, games, logs, _ := setupExtractorTest(t, enricher1) // Two games, one of which is ignored games.games = []gameTypes.GameMetadata{{Proxy: ignoredGames[0]}, {Proxy: common.Address{0xaa}}} enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) @@ -152,6 +153,47 @@ func TestExtractor_Extract(t *testing.T) { testlog.NewMessageFilter("Ignoring game"), testlog.NewAttributesFilter("game", ignoredGames[0].Hex()))) }) + + t.Run("UseCachedValueOnFailure", func(t *testing.T) { + enricher := &mockEnricher{} + extractor, _, games, _, cl := setupExtractorTest(t, enricher) + gameA := common.Address{0xaa} + gameB := common.Address{0xbb} + games.games = []gameTypes.GameMetadata{{Proxy: gameA}, {Proxy: gameB}} + + // First fetch succeeds and the results should be cached + enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0) + require.NoError(t, err) + require.Zero(t, ignored) + require.Zero(t, failed) + require.Len(t, enriched, 2) + require.Equal(t, 2, enricher.calls) + firstUpdateTime := cl.Now() + require.Equal(t, firstUpdateTime, enriched[0].LastUpdateTime) + require.Equal(t, firstUpdateTime, enriched[1].LastUpdateTime) + + cl.AdvanceTime(2 * time.Minute) + secondUpdateTime := cl.Now() + enricher.action = func(game *monTypes.EnrichedGameData) error { + if game.Proxy == gameA { + return errors.New("boom") + } + // Updated games will have a different status + game.Status = gameTypes.GameStatusChallengerWon + return nil + } + // Second fetch fails for one of the two games, it's cached value should be used. + enriched, ignored, failed, err = extractor.Extract(context.Background(), common.Hash{}, 0) + require.NoError(t, err) + require.Zero(t, ignored) + require.Equal(t, 1, failed) + require.Len(t, enriched, 2) + require.Equal(t, 4, enricher.calls) + require.Equal(t, enriched[0].Status, gameTypes.GameStatusInProgress) // Uses cached value from game A + require.Equal(t, enriched[1].Status, gameTypes.GameStatusChallengerWon) // Updates game B + require.Equal(t, firstUpdateTime, enriched[0].LastUpdateTime) + require.Equal(t, secondUpdateTime, enriched[1].LastUpdateTime) + }) } func verifyLogs(t *testing.T, logs *testlog.CapturingHandler, createErr, metadataErr, claimsErr, durationErr int) { @@ -170,20 +212,22 @@ func verifyLogs(t *testing.T, logs *testlog.CapturingHandler, createErr, metadat require.Len(t, l, durationErr) } -func setupExtractorTest(t *testing.T, enrichers ...Enricher) (*Extractor, *mockGameCallerCreator, *mockGameFetcher, *testlog.CapturingHandler) { +func setupExtractorTest(t *testing.T, enrichers ...Enricher) (*Extractor, *mockGameCallerCreator, *mockGameFetcher, *testlog.CapturingHandler, *clock.DeterministicClock) { logger, capturedLogs := testlog.CaptureLogger(t, log.LvlDebug) games := &mockGameFetcher{} caller := &mockGameCaller{rootClaim: mockRootClaim} creator := &mockGameCallerCreator{caller: caller} + cl := clock.NewDeterministicClock(time.Unix(48294294, 58)) extractor := NewExtractor( logger, + cl, creator.CreateGameCaller, games.FetchGames, ignoredGames, 5, enrichers..., ) - return extractor, creator, games, capturedLogs + return extractor, creator, games, capturedLogs, cl } type mockGameFetcher struct { @@ -311,11 +355,15 @@ func (m *mockGameCaller) IsResolved(_ context.Context, _ rpcblock.Block, claims } type mockEnricher struct { - err error - calls int + err error + calls int + action func(game *monTypes.EnrichedGameData) error } -func (m *mockEnricher) Enrich(_ context.Context, _ rpcblock.Block, _ GameCaller, _ *monTypes.EnrichedGameData) error { +func (m *mockEnricher) Enrich(_ context.Context, _ rpcblock.Block, _ GameCaller, game *monTypes.EnrichedGameData) error { m.calls++ + if m.action != nil { + return m.action(game) + } return m.err } diff --git a/op-dispute-mon/mon/monitor.go b/op-dispute-mon/mon/monitor.go index 039255608c73..5f7764adccbd 100644 --- a/op-dispute-mon/mon/monitor.go +++ b/op-dispute-mon/mon/monitor.go @@ -14,8 +14,6 @@ import ( ) type ForecastResolution func(games []*types.EnrichedGameData, ignoredCount, failedCount int) -type Bonds func(games []*types.EnrichedGameData) -type Resolutions func(games []*types.EnrichedGameData) type Monitor func(games []*types.EnrichedGameData) type BlockHashFetcher func(ctx context.Context, number *big.Int) (common.Hash, error) type BlockNumberFetcher func(ctx context.Context) (uint64, error) @@ -38,11 +36,7 @@ type gameMonitor struct { monitorInterval time.Duration forecast ForecastResolution - bonds Bonds - resolutions Resolutions - claims Monitor - withdrawals Monitor - l2Challenges Monitor + monitors []Monitor extract Extract fetchBlockHash BlockHashFetcher fetchBlockNumber BlockNumberFetcher @@ -55,16 +49,11 @@ func newGameMonitor( metrics MonitorMetrics, monitorInterval time.Duration, gameWindow time.Duration, - forecast ForecastResolution, - bonds Bonds, - resolutions Resolutions, - claims Monitor, - withdrawals Monitor, - l2Challenges Monitor, - extract Extract, - fetchBlockNumber BlockNumberFetcher, fetchBlockHash BlockHashFetcher, -) *gameMonitor { + fetchBlockNumber BlockNumberFetcher, + extract Extract, + forecast ForecastResolution, + monitors ...Monitor) *gameMonitor { return &gameMonitor{ logger: logger, clock: cl, @@ -74,11 +63,7 @@ func newGameMonitor( monitorInterval: monitorInterval, gameWindow: gameWindow, forecast: forecast, - bonds: bonds, - resolutions: resolutions, - claims: claims, - withdrawals: withdrawals, - l2Challenges: l2Challenges, + monitors: monitors, extract: extract, fetchBlockNumber: fetchBlockNumber, fetchBlockHash: fetchBlockHash, @@ -101,12 +86,10 @@ func (m *gameMonitor) monitorGames() error { if err != nil { return fmt.Errorf("failed to load games: %w", err) } - m.resolutions(enrichedGames) m.forecast(enrichedGames, ignored, failed) - m.bonds(enrichedGames) - m.claims(enrichedGames) - m.withdrawals(enrichedGames) - m.l2Challenges(enrichedGames) + for _, monitor := range m.monitors { + monitor(enrichedGames) + } timeTaken := m.clock.Since(start) m.metrics.RecordMonitorDuration(timeTaken) m.logger.Info("Completed monitoring update", "blockNumber", blockNumber, "blockHash", blockHash, "duration", timeTaken, "games", len(enrichedGames), "ignored", ignored, "failed", failed) diff --git a/op-dispute-mon/mon/monitor_test.go b/op-dispute-mon/mon/monitor_test.go index 180c29bb26ce..1181c57b4ecb 100644 --- a/op-dispute-mon/mon/monitor_test.go +++ b/op-dispute-mon/mon/monitor_test.go @@ -25,7 +25,7 @@ func TestMonitor_MonitorGames(t *testing.T) { t.Parallel() t.Run("FailedFetchBlocknumber", func(t *testing.T) { - monitor, _, _, _, _, _, _, _ := setupMonitorTest(t) + monitor, _, _, _ := setupMonitorTest(t) boom := errors.New("boom") monitor.fetchBlockNumber = func(ctx context.Context) (uint64, error) { return 0, boom @@ -35,7 +35,7 @@ func TestMonitor_MonitorGames(t *testing.T) { }) t.Run("FailedFetchBlockHash", func(t *testing.T) { - monitor, _, _, _, _, _, _, _ := setupMonitorTest(t) + monitor, _, _, _ := setupMonitorTest(t) boom := errors.New("boom") monitor.fetchBlockHash = func(ctx context.Context, number *big.Int) (common.Hash, error) { return common.Hash{}, boom @@ -45,29 +45,25 @@ func TestMonitor_MonitorGames(t *testing.T) { }) t.Run("MonitorsWithNoGames", func(t *testing.T) { - monitor, factory, forecast, bonds, withdrawals, resolutions, claims, l2Challenges := setupMonitorTest(t) + monitor, factory, forecast, monitors := setupMonitorTest(t) factory.games = []*monTypes.EnrichedGameData{} err := monitor.monitorGames() require.NoError(t, err) require.Equal(t, 1, forecast.calls) - require.Equal(t, 1, bonds.calls) - require.Equal(t, 1, resolutions.calls) - require.Equal(t, 1, claims.calls) - require.Equal(t, 1, withdrawals.calls) - require.Equal(t, 1, l2Challenges.calls) + for _, m := range monitors { + require.Equal(t, 1, m.calls) + } }) t.Run("MonitorsMultipleGames", func(t *testing.T) { - monitor, factory, forecast, bonds, withdrawals, resolutions, claims, l2Challenges := setupMonitorTest(t) + monitor, factory, forecast, monitors := setupMonitorTest(t) factory.games = []*monTypes.EnrichedGameData{{}, {}, {}} err := monitor.monitorGames() require.NoError(t, err) require.Equal(t, 1, forecast.calls) - require.Equal(t, 1, bonds.calls) - require.Equal(t, 1, resolutions.calls) - require.Equal(t, 1, claims.calls) - require.Equal(t, 1, withdrawals.calls) - require.Equal(t, 1, l2Challenges.calls) + for _, m := range monitors { + require.Equal(t, 1, m.calls) + } }) } @@ -75,7 +71,7 @@ func TestMonitor_StartMonitoring(t *testing.T) { t.Run("MonitorsGames", func(t *testing.T) { addr1 := common.Address{0xaa} addr2 := common.Address{0xbb} - monitor, factory, forecaster, _, _, _, _, _ := setupMonitorTest(t) + monitor, factory, forecaster, _ := setupMonitorTest(t) factory.games = []*monTypes.EnrichedGameData{newEnrichedGameData(addr1, 9999), newEnrichedGameData(addr2, 9999)} factory.maxSuccess = len(factory.games) // Only allow two successful fetches @@ -88,7 +84,7 @@ func TestMonitor_StartMonitoring(t *testing.T) { }) t.Run("FailsToFetchGames", func(t *testing.T) { - monitor, factory, forecaster, _, _, _, _, _ := setupMonitorTest(t) + monitor, factory, forecaster, _ := setupMonitorTest(t) factory.fetchErr = errors.New("boom") monitor.StartMonitoring() @@ -110,7 +106,7 @@ func newEnrichedGameData(proxy common.Address, timestamp uint64) *monTypes.Enric } } -func setupMonitorTest(t *testing.T) (*gameMonitor, *mockExtractor, *mockForecast, *mockBonds, *mockMonitor, *mockResolutionMonitor, *mockMonitor, *mockMonitor) { +func setupMonitorTest(t *testing.T) (*gameMonitor, *mockExtractor, *mockForecast, []*mockMonitor) { logger := testlog.Logger(t, log.LvlDebug) fetchBlockNum := func(ctx context.Context) (uint64, error) { return 1, nil @@ -123,37 +119,12 @@ func setupMonitorTest(t *testing.T) (*gameMonitor, *mockExtractor, *mockForecast cl.Start() extractor := &mockExtractor{} forecast := &mockForecast{} - bonds := &mockBonds{} - resolutions := &mockResolutionMonitor{} - claims := &mockMonitor{} - withdrawals := &mockMonitor{} - l2Challenges := &mockMonitor{} - monitor := newGameMonitor( - context.Background(), - logger, - cl, - metrics.NoopMetrics, - monitorInterval, - 10*time.Second, - forecast.Forecast, - bonds.CheckBonds, - resolutions.CheckResolutions, - claims.Check, - withdrawals.Check, - l2Challenges.Check, - extractor.Extract, - fetchBlockNum, - fetchBlockHash, - ) - return monitor, extractor, forecast, bonds, withdrawals, resolutions, claims, l2Challenges -} - -type mockResolutionMonitor struct { - calls int -} - -func (m *mockResolutionMonitor) CheckResolutions(games []*monTypes.EnrichedGameData) { - m.calls++ + monitor1 := &mockMonitor{} + monitor2 := &mockMonitor{} + monitor3 := &mockMonitor{} + monitor := newGameMonitor(context.Background(), logger, cl, metrics.NoopMetrics, monitorInterval, 10*time.Second, fetchBlockHash, fetchBlockNum, + extractor.Extract, forecast.Forecast, monitor1.Check, monitor2.Check, monitor3.Check) + return monitor, extractor, forecast, []*mockMonitor{monitor1, monitor2, monitor3} } type mockMonitor struct { @@ -172,14 +143,6 @@ func (m *mockForecast) Forecast(_ []*monTypes.EnrichedGameData, _, _ int) { m.calls++ } -type mockBonds struct { - calls int -} - -func (m *mockBonds) CheckBonds(_ []*monTypes.EnrichedGameData) { - m.calls++ -} - type mockExtractor struct { fetchErr error calls int diff --git a/op-dispute-mon/mon/service.go b/op-dispute-mon/mon/service.go index 8ce97b7b8dd9..083e391b9111 100644 --- a/op-dispute-mon/mon/service.go +++ b/op-dispute-mon/mon/service.go @@ -126,6 +126,7 @@ func (s *Service) initGameCallerCreator() { func (s *Service) initExtractor(cfg *config.Config) { s.extractor = extract.NewExtractor( s.logger, + s.cl, s.game.CreateContract, s.factoryContract.GetGamesAtOrAfter, cfg.IgnoredGames, @@ -217,23 +218,17 @@ func (s *Service) initMonitor(ctx context.Context, cfg *config.Config) { return block.Hash(), nil } l2ChallengesMonitor := NewL2ChallengesMonitor(s.logger, s.metrics) - s.monitor = newGameMonitor( - ctx, - s.logger, - s.cl, - s.metrics, - cfg.MonitorInterval, - cfg.GameWindow, + updateTimeMonitor := NewUpdateTimeMonitor(s.cl, s.metrics) + s.monitor = newGameMonitor(ctx, s.logger, s.cl, s.metrics, cfg.MonitorInterval, cfg.GameWindow, blockHashFetcher, + s.l1Client.BlockNumber, + s.extractor.Extract, s.forecast.Forecast, s.bonds.CheckBonds, s.resolutions.CheckResolutions, s.claims.CheckClaims, s.withdrawals.CheckWithdrawals, l2ChallengesMonitor.CheckL2Challenges, - s.extractor.Extract, - s.l1Client.BlockNumber, - blockHashFetcher, - ) + updateTimeMonitor.CheckUpdateTimes) } func (s *Service) Start(ctx context.Context) error { diff --git a/op-dispute-mon/mon/types/types.go b/op-dispute-mon/mon/types/types.go index dff06dab90bf..774ad3908cf7 100644 --- a/op-dispute-mon/mon/types/types.go +++ b/op-dispute-mon/mon/types/types.go @@ -18,6 +18,7 @@ type EnrichedClaim struct { type EnrichedGameData struct { types.GameMetadata + LastUpdateTime time.Time L1Head common.Hash L1HeadNum uint64 L2BlockNumber uint64 diff --git a/op-dispute-mon/mon/update_times.go b/op-dispute-mon/mon/update_times.go new file mode 100644 index 000000000000..3482775757b0 --- /dev/null +++ b/op-dispute-mon/mon/update_times.go @@ -0,0 +1,34 @@ +package mon + +import ( + "time" + + "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/clock" +) + +type UpdateTimeMetrics interface { + RecordOldestGameUpdateTime(t time.Time) +} + +type UpdateTimeMonitor struct { + metrics UpdateTimeMetrics + clock clock.Clock +} + +func NewUpdateTimeMonitor(cl clock.Clock, metrics UpdateTimeMetrics) *UpdateTimeMonitor { + return &UpdateTimeMonitor{clock: cl, metrics: metrics} +} + +func (m *UpdateTimeMonitor) CheckUpdateTimes(games []*types.EnrichedGameData) { + // Report the current time if there are no games + // Otherwise the last update time would drop to 0 when there are no games, making it appear there were errors + earliest := m.clock.Now() + + for _, game := range games { + if game.LastUpdateTime.Before(earliest) { + earliest = game.LastUpdateTime + } + } + m.metrics.RecordOldestGameUpdateTime(earliest) +} diff --git a/op-dispute-mon/mon/update_times_test.go b/op-dispute-mon/mon/update_times_test.go new file mode 100644 index 000000000000..a16da62b3c2e --- /dev/null +++ b/op-dispute-mon/mon/update_times_test.go @@ -0,0 +1,43 @@ +package mon + +import ( + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/clock" + "github.com/stretchr/testify/require" +) + +func TestUpdateTimeMonitor_NoGames(t *testing.T) { + m := &mockUpdateTimeMetrics{} + cl := clock.NewDeterministicClock(time.UnixMilli(45892)) + monitor := NewUpdateTimeMonitor(cl, m) + monitor.CheckUpdateTimes(nil) + require.Equal(t, cl.Now(), m.oldestUpdateTime) + + cl.AdvanceTime(time.Minute) + monitor.CheckUpdateTimes([]*types.EnrichedGameData{}) + require.Equal(t, cl.Now(), m.oldestUpdateTime) +} + +func TestUpdateTimeMonitor_ReportsOldestUpdateTime(t *testing.T) { + m := &mockUpdateTimeMetrics{} + cl := clock.NewDeterministicClock(time.UnixMilli(45892)) + monitor := NewUpdateTimeMonitor(cl, m) + monitor.CheckUpdateTimes([]*types.EnrichedGameData{ + {LastUpdateTime: time.UnixMilli(4)}, + {LastUpdateTime: time.UnixMilli(3)}, + {LastUpdateTime: time.UnixMilli(7)}, + {LastUpdateTime: time.UnixMilli(9)}, + }) + require.Equal(t, time.UnixMilli(3), m.oldestUpdateTime) +} + +type mockUpdateTimeMetrics struct { + oldestUpdateTime time.Time +} + +func (m *mockUpdateTimeMetrics) RecordOldestGameUpdateTime(t time.Time) { + m.oldestUpdateTime = t +} From 20e35fd8540d2868aa3d5f0806da18c4e6bf16e7 Mon Sep 17 00:00:00 2001 From: John Chase <68833933+joohhnnn@users.noreply.github.com> Date: Thu, 24 Oct 2024 04:06:27 +0800 Subject: [PATCH 003/451] MTCannon: add WakeupTraversal_WithExitedThreads test (#12551) * add WakeupTraversal_WithExitedThreads * In the TestEVM_WakeupTraversal_WithExitedThreads test, explicitly set the current active thread to exited state (activeThread.Exited = true) to test the wakeup traversal behavior when the active thread has exited && Modified test cases by resetting the active thread's index from exitedThreadIdx to avoid duplicate settings and confusion. * setting Futex fields with varying values. --- .../mipsevm/tests/evm_multithreaded_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 813e307849db..6c297ea946d0 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -1495,6 +1495,80 @@ func TestEVM_WakeupTraversal_Full(t *testing.T) { } } +func TestEVM_WakeupTraversal_WithExitedThreads(t *testing.T) { + var tracer *tracing.Hooks + addr := Word(0x1234) + wakeupVal := Word(0x999) + cases := []struct { + name string + wakeupAddr Word + futexAddr Word + targetVal Word + traverseRight bool + activeStackSize int + otherStackSize int + exitedThreadIdx []int + shouldClearWakeup bool + shouldPreempt bool + activeThreadFutexAddr Word + activeThreadFutexVal Word + }{ + {name: "Wakeable thread exists among exited threads", wakeupAddr: addr, futexAddr: addr, targetVal: wakeupVal + 1, traverseRight: false, activeStackSize: 3, otherStackSize: 1, exitedThreadIdx: []int{2}, shouldClearWakeup: false, shouldPreempt: true, activeThreadFutexAddr: addr + 8, activeThreadFutexVal: wakeupVal + 2}, + {name: "All threads exited", wakeupAddr: addr, futexAddr: addr, targetVal: wakeupVal, traverseRight: false, activeStackSize: 3, otherStackSize: 0, exitedThreadIdx: []int{1, 2}, shouldClearWakeup: false, shouldPreempt: true, activeThreadFutexAddr: addr + 16, activeThreadFutexVal: wakeupVal + 3}, + {name: "Exited threads, no matching futex", wakeupAddr: addr, futexAddr: addr + 4, targetVal: wakeupVal, traverseRight: false, activeStackSize: 2, otherStackSize: 1, exitedThreadIdx: []int{}, shouldClearWakeup: false, shouldPreempt: true, activeThreadFutexAddr: addr + 24, activeThreadFutexVal: wakeupVal + 4}, + {name: "Matching addr, not wakeable, with exited threads", wakeupAddr: addr, futexAddr: addr, targetVal: wakeupVal, traverseRight: true, activeStackSize: 3, otherStackSize: 0, exitedThreadIdx: []int{1}, shouldClearWakeup: false, shouldPreempt: true, activeThreadFutexAddr: addr + 32, activeThreadFutexVal: wakeupVal + 5}, + {name: "Non-waiting threads with exited threads", wakeupAddr: addr, futexAddr: exec.FutexEmptyAddr, targetVal: 0, traverseRight: false, activeStackSize: 2, otherStackSize: 1, exitedThreadIdx: []int{}, shouldClearWakeup: false, shouldPreempt: true, activeThreadFutexAddr: addr + 40, activeThreadFutexVal: wakeupVal + 6}, + } + + for i, c := range cases { + t.Run(c.name, func(t *testing.T) { + goVm, state, contracts := setup(t, i*1000, nil) + mttestutil.SetupThreads(int64(i*5000), state, c.traverseRight, c.activeStackSize, c.otherStackSize) + step := state.Step + + state.Wakeup = c.wakeupAddr + state.GetMemory().SetWord(c.wakeupAddr&arch.AddressMask, wakeupVal) + + threads := mttestutil.GetAllThreads(state) + for idx, thread := range threads { + if slices.Contains(c.exitedThreadIdx, idx) { + thread.Exited = true + } else { + thread.FutexAddr = c.futexAddr + thread.FutexVal = c.targetVal + thread.FutexTimeoutStep = exec.FutexNoTimeout + } + } + + activeThread := state.GetCurrentThread() + activeThread.Exited = true + + activeThread.FutexAddr = c.activeThreadFutexAddr + activeThread.FutexVal = c.activeThreadFutexVal + activeThread.FutexTimeoutStep = exec.FutexNoTimeout + + expected := mttestutil.NewExpectedMTState(state) + expected.Step += 1 + + if c.shouldClearWakeup { + expected.Wakeup = exec.FutexEmptyAddr + } + if c.shouldPreempt { + // Just preempt the current thread + expected.ExpectPreemption(state) + } + + // State transition + var err error + var stepWitness *mipsevm.StepWitness + stepWitness, err = goVm.Step(true) + require.NoError(t, err) + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + }) + } +} + func TestEVM_SchedQuantumThreshold(t *testing.T) { var tracer *tracing.Hooks cases := []struct { From 358dd3f7d9d94cdbdd92f56c5544dc5078379d16 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:18:21 -0300 Subject: [PATCH 004/451] refactor: remove superchain erc20 modifier (#12566) * fix: remove superchain erc20 modifier (#111) * fix: remove superchain erc20 modifier --------- Co-authored-by: Disco <131301107+0xDiscotech@users.noreply.github.com> Co-authored-by: 0xng Co-authored-by: 0xParticle Co-authored-by: gotzenx <78360669+gotzenx@users.noreply.github.com> * fix: pre pr --------- Co-authored-by: Disco <131301107+0xDiscotech@users.noreply.github.com> Co-authored-by: 0xng Co-authored-by: 0xParticle Co-authored-by: gotzenx <78360669+gotzenx@users.noreply.github.com> --- packages/contracts-bedrock/semver-lock.json | 2 +- .../src/L2/SuperchainERC20.sol | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index e73dd3b4527e..433b6ce92a16 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -125,7 +125,7 @@ }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "sourceCodeHash": "0x6a384ccfb6f2f7316c1b33873a1630b5179e52475951d31771656e06d2b11519" + "sourceCodeHash": "0x45b9afdc9e52c27f192673388868a803f54d8f0bffe9defd81d584642e282b6b" }, "src/L2/SuperchainTokenBridge.sol": { "initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", diff --git a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol index d723ed8d992b..a824dd3f9018 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol @@ -12,22 +12,18 @@ import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; /// bridging to make it fungible across the Superchain. This construction allows the SuperchainTokenBridge to /// burn and mint tokens. abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { - /// @notice A modifier that only allows the SuperchainTokenBridge to call - modifier onlySuperchainTokenBridge() { - if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized(); - _; - } - /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.2 + /// @custom:semver 1.0.0-beta.3 function version() external view virtual returns (string memory) { - return "1.0.0-beta.2"; + return "1.0.0-beta.3"; } /// @notice Allows the SuperchainTokenBridge to mint tokens. /// @param _to Address to mint tokens to. /// @param _amount Amount of tokens to mint. - function crosschainMint(address _to, uint256 _amount) external onlySuperchainTokenBridge { + function crosschainMint(address _to, uint256 _amount) external { + if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized(); + _mint(_to, _amount); emit CrosschainMinted(_to, _amount); @@ -36,7 +32,9 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { /// @notice Allows the SuperchainTokenBridge to burn tokens. /// @param _from Address to burn tokens from. /// @param _amount Amount of tokens to burn. - function crosschainBurn(address _from, uint256 _amount) external onlySuperchainTokenBridge { + function crosschainBurn(address _from, uint256 _amount) external { + if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized(); + _burn(_from, _amount); emit CrosschainBurnt(_from, _amount); From de38be099837055499c185e0a65917081fb72a3c Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:07:04 -0300 Subject: [PATCH 005/451] refactor: rename crosschain events (#12564) * fix: rename crosschain events (#109) * fix: rename crosschain events * fix: semver --------- Co-authored-by: Disco <131301107+0xDiscotech@users.noreply.github.com> Co-authored-by: 0xng Co-authored-by: 0xParticle Co-authored-by: gotzenx <78360669+gotzenx@users.noreply.github.com> * fix: pre pr * fix: pre pr --------- Co-authored-by: Disco <131301107+0xDiscotech@users.noreply.github.com> Co-authored-by: 0xng Co-authored-by: 0xParticle Co-authored-by: gotzenx <78360669+gotzenx@users.noreply.github.com> --- packages/contracts-bedrock/semver-lock.json | 10 +++---- .../abi/OptimismSuperchainERC20.json | 4 +-- .../snapshots/abi/SuperchainWETH.json | 4 +-- .../src/L2/OptimismSuperchainERC20.sol | 4 +-- .../src/L2/SuperchainERC20.sol | 8 +++--- .../src/L2/SuperchainWETH.sol | 8 +++--- .../src/L2/interfaces/ICrosschainERC20.sol | 4 +-- .../test/L2/SuperchainERC20.t.sol | 10 +++---- .../test/L2/SuperchainWETH.t.sol | 28 +++++++++---------- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 433b6ce92a16..45944697945e 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -108,8 +108,8 @@ "sourceCodeHash": "0x4bb08a8a9d5de37d1fb2dd8a9bcc483305510c32508f0be2d54757e6e257aedb" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x4e25579079d73c93f1d494e1976334b77fc4ec181c67f376d8e2613c7b207f52", - "sourceCodeHash": "0xe41cf3b005f1ea007fc1b5f69f630be5f6ef12d6e5e94a50e3160b0ebe0a1613" + "initCodeHash": "0x24d85d246858d1aff78ae86c614dd0dc0f63b3326b2b662e3462c3a6f9b7965e", + "sourceCodeHash": "0xcb705d26e63e733051c8bd442ea69ce637a00c16d646ccc37b687b20941366fe" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", @@ -125,15 +125,15 @@ }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "sourceCodeHash": "0x45b9afdc9e52c27f192673388868a803f54d8f0bffe9defd81d584642e282b6b" + "sourceCodeHash": "0xba47f404e66e010ce417410476f26c704f2be4ce584cb79210bc5536a82ddb1f" }, "src/L2/SuperchainTokenBridge.sol": { "initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0x09c7efed7d6c8ae5981f6e7a75c7b8c675f73d679265d15a010844ad9b41fa9b", - "sourceCodeHash": "0x8d7612a71deaadfb324c4136673df96019211292ff54494fa4b7724e2e5dd22a" + "initCodeHash": "0xc120d1fd9edd8aa392cf112859a3a3907c3b5d55132d6a5edd80ff1d6b6baeaa", + "sourceCodeHash": "0x445f088aa91fafde43f558f34da4b9f85b82e6a76f8b2dec9563838eb4335928" }, "src/L2/WETH.sol": { "initCodeHash": "0xfb253765520690623f177941c2cd9eba23e4c6d15063bccdd5e98081329d8956", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json b/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json index d6ad63fad9c3..24ac8a88fab4 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json @@ -456,7 +456,7 @@ "type": "uint256" } ], - "name": "CrosschainBurnt", + "name": "CrosschainBurn", "type": "event" }, { @@ -475,7 +475,7 @@ "type": "uint256" } ], - "name": "CrosschainMinted", + "name": "CrosschainMint", "type": "event" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json index 59e959f9f231..d76246faf218 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json @@ -289,7 +289,7 @@ "type": "uint256" } ], - "name": "CrosschainBurnt", + "name": "CrosschainBurn", "type": "event" }, { @@ -308,7 +308,7 @@ "type": "uint256" } ], - "name": "CrosschainMinted", + "name": "CrosschainMint", "type": "event" }, { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index a097bb736c84..1e7cdb4a7190 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -59,8 +59,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.7 - string public constant override version = "1.0.0-beta.7"; + /// @custom:semver 1.0.0-beta.8 + string public constant override version = "1.0.0-beta.8"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol index a824dd3f9018..f18f91ad2413 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol @@ -13,9 +13,9 @@ import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; /// burn and mint tokens. abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.3 + /// @custom:semver 1.0.0-beta.4 function version() external view virtual returns (string memory) { - return "1.0.0-beta.3"; + return "1.0.0-beta.4"; } /// @notice Allows the SuperchainTokenBridge to mint tokens. @@ -26,7 +26,7 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { _mint(_to, _amount); - emit CrosschainMinted(_to, _amount); + emit CrosschainMint(_to, _amount); } /// @notice Allows the SuperchainTokenBridge to burn tokens. @@ -37,6 +37,6 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { _burn(_from, _amount); - emit CrosschainBurnt(_from, _amount); + emit CrosschainBurn(_from, _amount); } } diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index d27c6e7db514..a9242d9d0ab3 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -22,8 +22,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// do not use a custom gas token. contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.7 - string public constant version = "1.0.0-beta.7"; + /// @custom:semver 1.0.0-beta.8 + string public constant version = "1.0.0-beta.8"; /// @inheritdoc WETH98 function deposit() public payable override { @@ -66,7 +66,7 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount); } - emit CrosschainMinted(_to, _amount); + emit CrosschainMint(_to, _amount); } /// @notice Allows the SuperchainTokenBridge to burn tokens. @@ -82,6 +82,6 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: _amount }(); } - emit CrosschainBurnt(_from, _amount); + emit CrosschainBurn(_from, _amount); } } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol b/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol index efdebe8f8b47..9fbfe8c65f33 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol @@ -7,12 +7,12 @@ interface ICrosschainERC20 { /// @notice Emitted when a crosschain transfer mints tokens. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. - event CrosschainMinted(address indexed to, uint256 amount); + event CrosschainMint(address indexed to, uint256 amount); /// @notice Emitted when a crosschain transfer burns tokens. /// @param from Address of the account tokens are being burned from. /// @param amount Amount of tokens burned. - event CrosschainBurnt(address indexed from, uint256 amount); + event CrosschainBurn(address indexed from, uint256 amount); /// @notice Mint tokens through a crosschain transfer. /// @param _to Address to mint tokens to. diff --git a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol index 07b92ae3ca7d..f814c06c76df 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol @@ -60,9 +60,9 @@ contract SuperchainERC20Test is Test { vm.expectEmit(address(superchainERC20)); emit IERC20.Transfer(ZERO_ADDRESS, _to, _amount); - // Look for the emit of the `CrosschainMinted` event + // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainMinted(_to, _amount); + emit ICrosschainERC20.CrosschainMint(_to, _amount); // Call the `mint` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -86,7 +86,7 @@ contract SuperchainERC20Test is Test { superchainERC20.crosschainBurn(_from, _amount); } - /// @notice Tests the `burn` burns the amount and emits the `CrosschainBurnt` event. + /// @notice Tests the `burn` burns the amount and emits the `CrosschainBurn` event. function testFuzz_crosschainBurn_succeeds(address _from, uint256 _amount) public { // Ensure `_from` is not the zero address vm.assume(_from != ZERO_ADDRESS); @@ -103,9 +103,9 @@ contract SuperchainERC20Test is Test { vm.expectEmit(address(superchainERC20)); emit IERC20.Transfer(_from, ZERO_ADDRESS, _amount); - // Look for the emit of the `CrosschainBurnt` event + // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainBurnt(_from, _amount); + emit ICrosschainERC20.CrosschainBurn(_from, _amount); // Call the `burn` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index 346631ac52ce..aa8eaab09939 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -25,10 +25,10 @@ contract SuperchainWETH_Test is CommonTest { event Withdrawal(address indexed src, uint256 wad); /// @notice Emitted when a crosschain transfer mints tokens. - event CrosschainMinted(address indexed to, uint256 amount); + event CrosschainMint(address indexed to, uint256 amount); /// @notice Emitted when a crosschain transfer burns tokens. - event CrosschainBurnt(address indexed from, uint256 amount); + event CrosschainBurn(address indexed from, uint256 amount); address internal constant ZERO_ADDRESS = address(0); @@ -143,7 +143,7 @@ contract SuperchainWETH_Test is CommonTest { superchainWeth.crosschainMint(_to, _amount); } - /// @notice Tests the `crosschainMint` with non custom gas token succeeds and emits the `CrosschainMinted` event. + /// @notice Tests the `crosschainMint` with non custom gas token succeeds and emits the `CrosschainMint` event. function testFuzz_crosschainMint_fromBridgeNonCustomGasTokenChain_succeeds(address _to, uint256 _amount) public { // Ensure `_to` is not the zero address vm.assume(_to != ZERO_ADDRESS); @@ -157,9 +157,9 @@ contract SuperchainWETH_Test is CommonTest { vm.expectEmit(address(superchainWeth)); emit Transfer(ZERO_ADDRESS, _to, _amount); - // Look for the emit of the `CrosschainMinted` event + // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainWeth)); - emit CrosschainMinted(_to, _amount); + emit CrosschainMint(_to, _amount); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); @@ -177,7 +177,7 @@ contract SuperchainWETH_Test is CommonTest { assertEq(address(superchainWeth).balance, _amount); } - /// @notice Tests the `crosschainMint` with custom gas token succeeds and emits the `CrosschainMinted` event. + /// @notice Tests the `crosschainMint` with custom gas token succeeds and emits the `CrosschainMint` event. function testFuzz_crosschainMint_fromBridgeCustomGasTokenChain_succeeds(address _to, uint256 _amount) public { // Ensure `_to` is not the zero address vm.assume(_to != ZERO_ADDRESS); @@ -190,9 +190,9 @@ contract SuperchainWETH_Test is CommonTest { vm.expectEmit(address(superchainWeth)); emit Transfer(ZERO_ADDRESS, _to, _amount); - // Look for the emit of the `CrosschainMinted` event + // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainWeth)); - emit CrosschainMinted(_to, _amount); + emit CrosschainMint(_to, _amount); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(true)); @@ -223,7 +223,7 @@ contract SuperchainWETH_Test is CommonTest { superchainWeth.crosschainBurn(_from, _amount); } - /// @notice Tests the `crosschainBurn` with non custom gas token burns the amount and emits the `CrosschainBurnt` + /// @notice Tests the `crosschainBurn` with non custom gas token burns the amount and emits the `CrosschainBurn` /// event. function testFuzz_crosschainBurn_fromBridgeNonCustomGasTokenChain_succeeds(address _from, uint256 _amount) public { // Ensure `_from` is not the zero address @@ -243,9 +243,9 @@ contract SuperchainWETH_Test is CommonTest { vm.expectEmit(address(superchainWeth)); emit Transfer(_from, ZERO_ADDRESS, _amount); - // Look for the emit of the `CrosschainBurnt` event + // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainWeth)); - emit CrosschainBurnt(_from, _amount); + emit CrosschainBurn(_from, _amount); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); @@ -263,7 +263,7 @@ contract SuperchainWETH_Test is CommonTest { assertEq(address(superchainWeth).balance, 0); } - /// @notice Tests the `crosschainBurn` with custom gas token burns the amount and emits the `CrosschainBurnt` + /// @notice Tests the `crosschainBurn` with custom gas token burns the amount and emits the `CrosschainBurn` /// event. function testFuzz_crosschainBurn_fromBridgeCustomGasTokenChain_succeeds(address _from, uint256 _amount) public { // Ensure `_from` is not the zero address @@ -285,9 +285,9 @@ contract SuperchainWETH_Test is CommonTest { vm.expectEmit(address(superchainWeth)); emit Transfer(_from, ZERO_ADDRESS, _amount); - // Look for the emit of the `CrosschainBurnt` event + // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainWeth)); - emit CrosschainBurnt(_from, _amount); + emit CrosschainBurn(_from, _amount); // Expect to not call the `burn` function in the `ETHLiquidity` contract vm.expectCall(Predeploys.ETH_LIQUIDITY, abi.encodeCall(IETHLiquidity.burn, ()), 0); From 7b119c533f22bd5ef86bed2455b945987ca319a9 Mon Sep 17 00:00:00 2001 From: Paul Dowman Date: Wed, 23 Oct 2024 15:16:01 -0600 Subject: [PATCH 006/451] docs: add info about contributing to larger projects (#12397) --- CONTRIBUTING.md | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0534e696ff5f..c4e5686db99e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,7 @@ You can: - Report issues in this repository. Great bug reports are detailed and give clear instructions for how a developer can reproduce the problem. Write good bug reports and developers will love you. - **IMPORTANT**: If you believe your report impacts the security of this repository, refer to the canonical [Security Policy](https://github.com/ethereum-optimism/.github/blob/master/SECURITY.md) document. - Fix issues that are tagged as [`D-good-first-issue`](https://github.com/ethereum-optimism/optimism/labels/D-good-first-issue) or [`S-confirmed`](https://github.com/ethereum-optimism/optimism/labels/S-confirmed). +- Larger projects are listed on [this project board](https://github.com/orgs/ethereum-optimism/projects/31/views/9). Please talk to us if you're considering working on one of these, they may not be fully specified so it will reduce risk to discuss the approach and ensure that it's still relevant. - Help improve the [Optimism Developer Docs](https://github.com/ethereum-optimism/docs) by reporting issues, fixing typos, or adding missing sections. - Get involved in the protocol design process by joining discussions within the [OP Stack Specs](https://github.com/ethereum-optimism/specs/discussions) repository. diff --git a/README.md b/README.md index 17d74edec5ee..789b0539d8ff 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ The OP Stack is a collaborative project. By collaborating on free, open software [CONTRIBUTING.md](./CONTRIBUTING.md) contains a detailed explanation of the contributing process for this repository. Make sure to use the [Developer Quick Start](./CONTRIBUTING.md#development-quick-start) to properly set up your development environment. -[Good First Issues](https://github.com/ethereum-optimism/optimism/issues?q=is:open+is:issue+label:D-good-first-issue) are a great place to look for tasks to tackle if you're not sure where to start. +[Good First Issues](https://github.com/ethereum-optimism/optimism/issues?q=is:open+is:issue+label:D-good-first-issue) are a great place to look for tasks to tackle if you're not sure where to start, and see [CONTRIBUTING.md](./CONTRIBUTING.md) for info on larger projects. ## Security Policy and Vulnerability Reporting From 239330b4b2e72b341d5d807a09a27e19ba9503e0 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 23 Oct 2024 14:37:28 -0700 Subject: [PATCH 007/451] cannon: Robust div by zero tests (#12600) --- cannon/mipsevm/exec/mips_instructions.go | 6 ++++++ cannon/mipsevm/tests/evm_common_test.go | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index af7b988c1584..1bf3849c4e3f 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -500,9 +500,15 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor cpu.HI = SignExtend(Word(acc>>32), 32) cpu.LO = SignExtend(Word(uint32(acc)), 32) case 0x1a: // div + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = SignExtend(Word(int32(rs)%int32(rt)), 32) cpu.LO = SignExtend(Word(int32(rs)/int32(rt)), 32) case 0x1b: // divu + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = SignExtend(Word(uint32(rs)%uint32(rt)), 32) cpu.LO = SignExtend(Word(uint32(rs)/uint32(rt)), 32) case 0x14: // dsllv diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index e204a0d96e9d..c9dfd2f09634 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -450,16 +450,16 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { expectLo Word expectRes Word rdReg uint32 - expectRevert bool + expectRevert string errMsg string }{ - {name: "mul", funct: uint32(0x2), rs: Word(5), rt: Word(2), opcode: uint32(28), rdReg: uint32(0x8), expectRes: Word(10), expectRevert: false}, // mul t0, t1, t2 - {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00), expectRevert: false}, // mult t1, t2 - {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00), expectRevert: false}, // multu t1, t2 - {name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2), expectRevert: false}, // div t1, t2 - {name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: true, errMsg: "MIPS: division by zero"}, // div t1, t2 - {name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2), expectRevert: false}, // divu t1, t2 - {name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: true, errMsg: "MIPS: division by zero"}, // divu t1, t2 + {name: "mul", funct: uint32(0x2), rs: Word(5), rt: Word(2), opcode: uint32(28), rdReg: uint32(0x8), expectRes: Word(10)}, // mul t0, t1, t2 + {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // mult t1, t2 + {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // multu t1, t2 + {name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2 + {name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // div t1, t2 + {name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2 + {name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // divu t1, t2 } for _, v := range versions { @@ -477,9 +477,9 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { state.GetRegistersRef()[baseReg] = tt.rs state.GetMemory().SetUint32(0, insn) - if tt.expectRevert { + if tt.expectRevert != "" { proofData := v.ProofGenerator(t, goVm.GetState()) - require.Panics(t, func() { + require.PanicsWithValue(t, tt.expectRevert, func() { _, _ = goVm.Step( false) }) From 93ca2941defb1579dc36375967eee55bbe5c3271 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 23 Oct 2024 15:17:05 -0700 Subject: [PATCH 008/451] cannon: Finish emulating rest of 64-bit instructions (#12483) * cannon: Finish emulating rest of 64-bit instructions This fixes the 64-bit stubs for various instructions (except lld/scd). * review comments; fix dmult * add todo * test div by zero * add a couple more dmultu tests * remove dead code * cannon: Fix remaining mips64 emulation bugs * fix 64-bit Makefile build script; review comments * fix build script --- cannon/Makefile | 4 +- cannon/mipsevm/README.md | 1 + cannon/mipsevm/arch/arch32.go | 2 + cannon/mipsevm/arch/arch64.go | 12 +- cannon/mipsevm/exec/mips_instructions.go | 177 +++-- .../multithreaded/instrumented_test.go | 9 +- cannon/mipsevm/multithreaded/mips.go | 8 +- .../singlethreaded/instrumented_test.go | 3 +- cannon/mipsevm/tests/evm_common64_test.go | 658 ++++++++++++++++++ cannon/mipsevm/tests/evm_common_test.go | 91 +++ .../mipsevm/tests/evm_multithreaded_test.go | 4 +- cannon/mipsevm/testutil/arch.go | 6 + cannon/mipsevm/testutil/elf.go | 10 + cannon/mipsevm/testutil/oracle.go | 2 +- cannon/mipsevm/testutil/vmtests.go | 21 +- cannon/testdata/example/Makefile | 11 +- cannon/testdata/example/alloc/go.mod | 2 +- cannon/testdata/example/claim/go.mod | 2 +- cannon/testdata/example/hello/go.mod | 4 +- go.mod | 13 +- go.sum | 36 +- packages/contracts-bedrock/semver-lock.json | 8 +- .../contracts-bedrock/src/cannon/MIPS.sol | 4 +- .../contracts-bedrock/src/cannon/MIPS2.sol | 8 +- .../src/cannon/libraries/MIPSInstructions.sol | 6 + .../src/cannon/libraries/MIPSSyscalls.sol | 2 + 26 files changed, 1009 insertions(+), 95 deletions(-) create mode 100644 cannon/mipsevm/tests/evm_common64_test.go diff --git a/cannon/Makefile b/cannon/Makefile index 5f74904011ec..ccf959fc9d4c 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -43,7 +43,7 @@ elf: make -C ./testdata/example elf sanitize-program: - @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bgezal|bltzal)'; }; then \ + @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bltzal)'; }; then \ echo "guest program is sanitized for unsupported instructions"; \ else \ echo "found unsupported instructions in the guest program"; \ @@ -93,6 +93,8 @@ fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests # Multi-threaded tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests + # 64-bit tests + go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateDmultInsn ./mipsevm/tests .PHONY: \ cannon32-impl \ diff --git a/cannon/mipsevm/README.md b/cannon/mipsevm/README.md index df7558187992..03afec250a4a 100644 --- a/cannon/mipsevm/README.md +++ b/cannon/mipsevm/README.md @@ -12,6 +12,7 @@ Supported 63 instructions: | `Conditional Branch` | `beq` | Branch on equal. | | `Conditional Branch` | `bgez` | Branch on greater than or equal to zero. | | `Conditional Branch` | `bgtz` | Branch on greater than zero. | +| `Conditional Branch` | `bgezal` | Branch and link on greater than or equal to zero. | | `Conditional Branch` | `blez` | Branch on less than or equal to zero. | | `Conditional Branch` | `bltz` | Branch on less than zero. | | `Conditional Branch` | `bne` | Branch on not equal. | diff --git a/cannon/mipsevm/arch/arch32.go b/cannon/mipsevm/arch/arch32.go index 87cad3cf504d..dbe9ee193d7a 100644 --- a/cannon/mipsevm/arch/arch32.go +++ b/cannon/mipsevm/arch/arch32.go @@ -79,6 +79,8 @@ const ( SysLlseek = 4140 SysMinCore = 4217 SysTgkill = 4266 + SysGetRLimit = 4076 + SysLseek = 4019 // Profiling-related syscalls SysSetITimer = 4104 SysTimerCreate = 4257 diff --git a/cannon/mipsevm/arch/arch64.go b/cannon/mipsevm/arch/arch64.go index a9b7df70c583..b260ebb1bbb3 100644 --- a/cannon/mipsevm/arch/arch64.go +++ b/cannon/mipsevm/arch/arch64.go @@ -25,10 +25,12 @@ const ( AddressMask = 0xFFFFFFFFFFFFFFF8 ExtMask = 0x7 - HeapStart = 0x10_00_00_00_00_00_00_00 - HeapEnd = 0x60_00_00_00_00_00_00_00 - ProgramBreak = 0x40_00_00_00_00_00_00_00 - HighMemoryStart = 0x7F_FF_FF_FF_D0_00_00_00 + // Ensure virtual address is limited to 48-bits as many user programs assume such to implement packed pointers + // limit 0x00_00_FF_FF_FF_FF_FF_FF + HeapStart = 0x00_00_10_00_00_00_00_00 + HeapEnd = 0x00_00_60_00_00_00_00_00 + ProgramBreak = 0x00_00_40_00_00_00_00_00 + HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00 ) // MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory. @@ -85,6 +87,8 @@ const ( SysLlseek = UndefinedSysNr SysMinCore = 5026 SysTgkill = 5225 + SysGetRLimit = 5095 + SysLseek = 5008 // Profiling-related syscalls SysSetITimer = 5036 SysTimerCreate = 5216 diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 1bf3849c4e3f..b99f519fe2f9 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -2,13 +2,11 @@ package exec import ( "fmt" + "math/bits" "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" - - // TODO(#12205): MIPS64 port. Replace with a custom library - u128 "lukechampine.com/uint128" ) const ( @@ -16,6 +14,11 @@ const ( OpStoreConditional = 0x38 OpLoadLinked64 = 0x34 OpStoreConditional64 = 0x3c + OpLoadDoubleLeft = 0x1A + OpLoadDoubleRight = 0x1B + + // Return address register + RegRA = 31 ) func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun uint32) { @@ -31,7 +34,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory if opcode == 2 || opcode == 3 { linkReg := Word(0) if opcode == 3 { - linkReg = 31 + linkReg = RegRA } // Take the top bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset target := (cpu.NextPC & SignExtend(0xF0000000, 32)) | Word((insn&0x03FFFFFF)<<2) @@ -77,7 +80,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory } if (opcode >= 4 && opcode < 8) || opcode == 1 { - err = HandleBranch(cpu, registers, opcode, insn, rtReg, rs) + err = HandleBranch(cpu, registers, opcode, insn, rtReg, rs, stackTracker) return } @@ -85,7 +88,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory // memory fetch (all I-type) // we do the load for stores also mem := Word(0) - if opcode >= 0x20 { + if opcode >= 0x20 || opcode == OpLoadDoubleLeft || opcode == OpLoadDoubleRight { // M[R[rs]+SignExtImm] rs += SignExtendImmediate(insn) addr := rs & arch.AddressMask @@ -311,10 +314,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x3C: // dsll32 assertMips64(insn) return rt << (((insn >> 6) & 0x1f) + 32) - case 0x3E: // dsll32 + case 0x3E: // dsrl32 assertMips64(insn) return rt >> (((insn >> 6) & 0x1f) + 32) - case 0x3F: // dsll32 + case 0x3F: // dsra32 assertMips64(insn) return Word(int64(rt) >> (((insn >> 6) & 0x1f) + 32)) default: @@ -347,13 +350,25 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := Word(arch.ExtMask - 1) return SignExtend((mem>>(msb-uint32(rs&mask)*8))&0xFFFF, 16) case 0x22: // lwl - val := mem << ((rs & 3) * 8) - mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) - return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + if arch.IsMips32 { + val := mem << ((rs & 3) * 8) + mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + } else { + // similar to the above mips32 implementation but loads are constrained to the nearest 4-byte memory word + shift := 32 - ((rs & 0x4) << 3) + w := uint32(mem >> shift) + val := uint64(w << ((rs & 3) * 8)) + mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) + return SignExtend(((rt & ^mask)|Word(val))&0xFFFFFFFF, 32) + } case 0x23: // lw - // TODO(#12205): port to MIPS64 - return mem - //return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) + if arch.IsMips32 { + return mem + } else { + // TODO(#12562): Simplify using LoadSubWord + return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) + } case 0x24: // lbu msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit return (mem >> (msb - uint32(rs&arch.ExtMask)*8)) & 0xFF @@ -362,9 +377,25 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := Word(arch.ExtMask - 1) return (mem >> (msb - uint32(rs&mask)*8)) & 0xFFFF case 0x26: // lwr - val := mem >> (24 - (rs&3)*8) - mask := Word(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) - return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + if arch.IsMips32 { + val := mem >> (24 - (rs&3)*8) + mask := Word(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + } else { + // similar to the above mips32 implementation but constrained to the nearest 4-byte memory word + shift := 32 - ((rs & 0x4) << 3) + w := uint32(mem >> shift) + val := w >> (24 - (rs&3)*8) + mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) + lwrResult := ((uint32(rt) & ^mask) | val) & 0xFFFFFFFF + if rs&3 == 3 { // loaded bit 31 + return SignExtend(Word(lwrResult), 32) + } else { + // NOTE: cannon64 implementation specific: We leave the upper word untouched + rtMask := uint64(0xFF_FF_FF_FF_00_00_00_00) + return (rt & Word(rtMask)) | Word(lwrResult) + } + } case 0x28: // sb msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit val := (rt & 0xFF) << (msb - uint32(rs&arch.ExtMask)*8) @@ -378,18 +409,45 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := ^Word(0) ^ Word(0xFFFF<> ((rs & 3) * 8) - mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) - return (mem & Word(^mask)) | val + if arch.IsMips32 { + val := rt >> ((rs & 3) * 8) + mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) + return (mem & Word(^mask)) | val + } else { + sr := (rs & 3) << 3 + val := ((rt & 0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) + mask := (uint64(0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) + return (mem & Word(^mask)) | val + } case 0x2b: // sw - // TODO(#12205): port to MIPS64 - return rt + if arch.IsMips32 { + return rt + } else { + sl := 32 - ((rs & 0x4) << 3) + val := (rt & 0xFFFFFFFF) << sl + mask := Word(0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFFFFFF<> shift) + val := rt << (24 - (rs&3)*8) + mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8) + swrResult := (w & ^mask) | uint32(val) + // merge with the untouched bytes in mem + if shift > 0 { + return (Word(swrResult) << 32) | (mem & Word(^uint32(0))) // nolint: staticcheck + } else { + memMask := uint64(0xFF_FF_FF_FF_00_00_00_00) + return (mem & Word(memMask)) | Word(swrResult & ^uint32(0)) + } + } // MIPS64 case 0x1A: // ldl @@ -424,10 +482,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return mem case 0x3F: // sd assertMips64(insn) - sl := (rs & 0x7) << 3 - val := rt << sl - mask := ^Word(0) << sl - return (mem & ^mask) | val + return rt default: panic("invalid instruction") } @@ -446,12 +501,13 @@ func SignExtend(dat Word, idx Word) Word { } } -func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, insn uint32, rtReg Word, rs Word) error { +func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, insn uint32, rtReg Word, rs Word, stackTracker StackTracker) error { if cpu.NextPC != cpu.PC+4 { panic("branch in delay slot") } shouldBranch := false + linked := false if opcode == 4 || opcode == 5 { // beq/bne rt := registers[rtReg] shouldBranch = (rs == rt && opcode == 4) || (rs != rt && opcode == 5) @@ -463,10 +519,15 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i // regimm rtv := (insn >> 16) & 0x1F if rtv == 0 { // bltz - shouldBranch = int32(rs) < 0 + shouldBranch = arch.SignedInteger(rs) < 0 } if rtv == 1 { // bgez - shouldBranch = int32(rs) >= 0 + shouldBranch = arch.SignedInteger(rs) >= 0 + } + if rtv == 0x11 { // bgezal (i.e. bal mnemonic) + shouldBranch = arch.SignedInteger(rs) >= 0 + registers[RegRA] = cpu.PC + 8 // always set regardless of branch taken + linked = true } } @@ -474,12 +535,16 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i cpu.PC = cpu.NextPC // execute the delay slot first if shouldBranch { cpu.NextPC = prevPC + 4 + (SignExtend(Word(insn&0xFFFF), 16) << 2) // then continue with the instruction the branch jumps to. + if linked { + stackTracker.PushStack(prevPC, cpu.NextPC) + } } else { cpu.NextPC = cpu.NextPC + 4 // branch not taken } return nil } +// HandleHiLo handles instructions that modify HI and LO registers. It also additionally handles doubleword variable shift operations func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Word, rt Word, storeReg Word) error { val := Word(0) switch fun { @@ -521,22 +586,44 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor assertMips64Fun(fun) val = Word(int64(rt) >> (rs & 0x3F)) case 0x1c: // dmult - // TODO(#12205): port to MIPS64. Is signed multiply needed for dmult assertMips64Fun(fun) - acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt))) - cpu.HI = Word(acc.Hi) - cpu.LO = Word(acc.Lo) + a := int64(rs) + b := int64(rt) + negative := (a < 0) != (b < 0) // set if operands have different signs + + // Handle special case for most negative value to avoid overflow in negation + absA := uint64(abs64(a)) + absB := uint64(abs64(b)) + + hi, lo := bits.Mul64(absA, absB) + if negative { + // Two's complement negation: flip all bits and add 1 + hi = ^hi + lo = ^lo + if lo == 0xFFFFFFFFFFFFFFFF { + hi++ + } + lo++ + } + cpu.HI = Word(hi) + cpu.LO = Word(lo) case 0x1d: // dmultu assertMips64Fun(fun) - acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt))) - cpu.HI = Word(acc.Hi) - cpu.LO = Word(acc.Lo) + hi, lo := bits.Mul64(uint64(rs), uint64(rt)) + cpu.HI = Word(hi) + cpu.LO = Word(lo) case 0x1e: // ddiv assertMips64Fun(fun) + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = Word(int64(rs) % int64(rt)) cpu.LO = Word(int64(rs) / int64(rt)) case 0x1f: // ddivu assertMips64Fun(fun) + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = rs % rt cpu.LO = rs / rt } @@ -619,3 +706,11 @@ func calculateSubWordMaskAndOffset(addr Word, byteLength Word) (dataMask, bitOff return dataMask, bitOffset, bitLength } + +// abs64 returns the absolute value +func abs64(x int64) int64 { + if x < 0 { + return -x + } + return x +} diff --git a/cannon/mipsevm/multithreaded/instrumented_test.go b/cannon/mipsevm/multithreaded/instrumented_test.go index dd4635668458..ec079e50c8ce 100644 --- a/cannon/mipsevm/multithreaded/instrumented_test.go +++ b/cannon/mipsevm/multithreaded/instrumented_test.go @@ -11,11 +11,12 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { - return NewInstrumentedState(state, po, stdOut, stdErr, log, nil) +func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM { + return NewInstrumentedState(state, po, stdOut, stdErr, log, meta) } func TestInstrumentedState_OpenMips(t *testing.T) { @@ -35,7 +36,7 @@ func TestInstrumentedState_Claim(t *testing.T) { func TestInstrumentedState_MultithreadedProgram(t *testing.T) { t.Parallel() - state, _ := testutil.LoadELFProgram(t, "../../testdata/example/bin/multithreaded.elf", CreateInitialState, false) + state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath("multithreaded"), CreateInitialState, false) oracle := testutil.StaticOracle(t, []byte{}) var stdOutBuf, stdErrBuf bytes.Buffer @@ -78,7 +79,7 @@ func TestInstrumentedState_Alloc(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - state, meta := testutil.LoadELFProgram(t, "../../testdata/example/bin/alloc.elf", CreateInitialState, false) + state, meta := testutil.LoadELFProgram(t, testutil.ProgramPath("alloc"), CreateInitialState, false) oracle := testutil.AllocOracle(t, test.numAllocs, test.allocSize) us := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), meta) diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index cddf9fd590f1..f371da2c4053 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -168,9 +168,9 @@ func (m *InstrumentedState) handleSyscall() error { m.memoryTracker.TrackMemAccess(effAddr) m.state.Memory.SetWord(effAddr, secs) m.handleMemoryUpdate(effAddr) - m.memoryTracker.TrackMemAccess2(effAddr + 4) - m.state.Memory.SetWord(effAddr+4, nsecs) - m.handleMemoryUpdate(effAddr + 4) + m.memoryTracker.TrackMemAccess2(effAddr + arch.WordSizeBytes) + m.state.Memory.SetWord(effAddr+arch.WordSizeBytes, nsecs) + m.handleMemoryUpdate(effAddr + arch.WordSizeBytes) default: v0 = exec.SysErrorSignal v1 = exec.MipsEINVAL @@ -206,6 +206,8 @@ func (m *InstrumentedState) handleSyscall() error { case arch.SysTimerCreate: case arch.SysTimerSetTime: case arch.SysTimerDelete: + case arch.SysGetRLimit: + case arch.SysLseek: default: // These syscalls have the same values on 64-bit. So we use if-stmts here to avoid "duplicate case" compiler error for the cannon64 build if arch.IsMips32 && syscallNum == arch.SysFstat64 || syscallNum == arch.SysStat64 || syscallNum == arch.SysLlseek { diff --git a/cannon/mipsevm/singlethreaded/instrumented_test.go b/cannon/mipsevm/singlethreaded/instrumented_test.go index 8b4c61bd4e14..d4c190d9cd91 100644 --- a/cannon/mipsevm/singlethreaded/instrumented_test.go +++ b/cannon/mipsevm/singlethreaded/instrumented_test.go @@ -7,10 +7,11 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { +func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM { return NewInstrumentedState(state, po, stdOut, stdErr, nil) } diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go new file mode 100644 index 000000000000..70f59120abc0 --- /dev/null +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -0,0 +1,658 @@ +//go:build cannon64 +// +build cannon64 + +package tests + +import ( + "fmt" + "os" + "testing" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/stretchr/testify/require" +) + +func TestEVMSingleStep_Operators64(t *testing.T) { + cases := []struct { + name string + isImm bool + rs Word + rt Word + imm uint16 + opcode uint32 + funct uint32 + expectRes Word + }{ + {name: "dadd. both unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 64 and unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x0FFFFFFF_00000012), rt: Word(0x20), expectRes: Word(0x0FFFFFFF_00000032)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(12), rt: ^Word(0), expectRes: Word(11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: ^Word(0), rt: Word(12), expectRes: Word(11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32. expect signed", funct: 0x2c, isImm: false, rs: ^Word(20), rt: Word(4), expectRes: ^Word(16)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed. expect signed", funct: 0x2c, isImm: false, rs: Word(4), rt: ^Word(20), expectRes: ^Word(16)}, // dadd t0, s1, s2 + {name: "dadd. both signed", funct: 0x2c, isImm: false, rs: ^Word(10), rt: ^Word(4), expectRes: ^Word(15)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 64. expect unsigned", funct: 0x2c, isImm: false, rs: ^Word(0), rt: Word(0x000000FF_00000000), expectRes: Word(0x000000FE_FFFFFFFF)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 64. expect signed", funct: 0x2c, isImm: false, rs: Word(0x80000000_00000000), rt: Word(0x40000000_00000000), expectRes: Word(0xC000000000000000)}, // dadd t0, s1, s2 + + {name: "daddu. both 32", funct: 0x2d, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // daddu t0, s1, s2 + {name: "daddu. 32-bit. expect doubleword-sized", funct: 0x2d, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // daddu t0, s1, s2 + {name: "daddu. 32-bit. expect double-word sized x", funct: 0x2d, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadu t0, s1, s2 + {name: "daddu. doubleword-sized, word-sized", funct: 0x2d, isImm: false, rs: Word(0x0FFFFFFF_00000012), rt: Word(0x20), expectRes: Word(0x0FFFFFFF_00000032)}, // dadu t0, s1, s2 + {name: "daddu. overflow. rt sign bit set", funct: 0x2d, isImm: false, rs: Word(12), rt: ^Word(0), expectRes: Word(11)}, // dadu t0, s1, s2 + {name: "daddu. overflow. rs sign bit set", funct: 0x2d, isImm: false, rs: ^Word(0), rt: Word(12), expectRes: Word(11)}, // dadu t0, s1, s2 + {name: "daddu. doubleword-sized and word-sized", funct: 0x2d, isImm: false, rs: ^Word(20), rt: Word(4), expectRes: ^Word(16)}, // dadu t0, s1, s2 + {name: "daddu. word-sized and doubleword-sized", funct: 0x2d, isImm: false, rs: Word(4), rt: ^Word(20), expectRes: ^Word(16)}, // dadu t0, s1, s2 + {name: "daddu. both doubleword-sized. expect overflow", funct: 0x2d, isImm: false, rs: ^Word(10), rt: ^Word(4), expectRes: ^Word(15)}, // dadu t0, s1, s2 + + {name: "daddi word-sized", opcode: 0x18, isImm: true, rs: Word(12), rt: ^Word(0), imm: uint16(20), expectRes: Word(32)}, // daddi t0, s1, s2 + {name: "daddi doubleword-sized", opcode: 0x18, isImm: true, rs: Word(0x00000010_00000000), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x00000010_00000020)}, // daddi t0, s1, s2 + {name: "daddi 32-bit sign", opcode: 0x18, isImm: true, rs: Word(0xFF_FF_FF_FF), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x01_00_00_00_1F)}, // daddi t0, s1, s2 + {name: "daddi double-word signed", opcode: 0x18, isImm: true, rs: ^Word(0), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x1F)}, // daddi t0, s1, s2 + {name: "daddi double-word signed. expect signed", opcode: 0x18, isImm: true, rs: ^Word(0x10), rt: ^Word(0), imm: uint16(0x1), expectRes: ^Word(0xF)}, // daddi t0, s1, s2 + + {name: "daddiu word-sized", opcode: 0x19, isImm: true, rs: Word(4), rt: ^Word(0), imm: uint16(40), expectRes: Word(44)}, // daddiu t0, s1, 40 + {name: "daddiu doubleword-sized", opcode: 0x19, isImm: true, rs: Word(0x00000010_00000000), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x00000010_00000020)}, // daddiu t0, s1, 40 + {name: "daddiu 32-bit sign", opcode: 0x19, isImm: true, rs: Word(0xFF_FF_FF_FF), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x01_00_00_00_1F)}, // daddiu t0, s1, 40 + {name: "daddiu overflow", opcode: 0x19, isImm: true, rs: ^Word(0), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x1F)}, // daddiu t0, s1, s2 + + {name: "dsub. both unsigned 32", funct: 0x2e, isImm: false, rs: Word(0x12), rt: Word(0x1), expectRes: Word(0x11)}, // dsub t0, s1, s2 + {name: "dsub. signed and unsigned 32", funct: 0x2e, isImm: false, rs: ^Word(1), rt: Word(0x1), expectRes: Word(^uint64(2))}, // dsub t0, s1, s2 + {name: "dsub. signed and unsigned 64", funct: 0x2e, isImm: false, rs: ^Word(1), rt: Word(0x00AABBCC_00000000), expectRes: ^Word(0x00AABBCC_00000001)}, // dsub t0, s1, s2 + {name: "dsub. both signed. unsigned result", funct: 0x2e, isImm: false, rs: ^Word(1), rt: ^Word(2), expectRes: Word(1)}, // dsub t0, s1, s2 + {name: "dsub. both signed. signed result", funct: 0x2e, isImm: false, rs: ^Word(2), rt: ^Word(1), expectRes: ^Word(0)}, // dsub t0, s1, s2 + {name: "dsub. signed and zero", funct: 0x2e, isImm: false, rs: ^Word(0), rt: Word(0), expectRes: ^Word(0)}, // dsub t0, s1, s2 + + {name: "dsubu. both unsigned 32", funct: 0x2f, isImm: false, rs: Word(0x12), rt: Word(0x1), expectRes: Word(0x11)}, // dsubu t0, s1, s2 + {name: "dsubu. signed and unsigned 32", funct: 0x2f, isImm: false, rs: ^Word(1), rt: Word(0x1), expectRes: Word(^uint64(2))}, // dsubu t0, s1, s2 + {name: "dsubu. signed and unsigned 64", funct: 0x2f, isImm: false, rs: ^Word(1), rt: Word(0x00AABBCC_00000000), expectRes: ^Word(0x00AABBCC_00000001)}, // dsubu t0, s1, s2 + {name: "dsubu. both signed. unsigned result", funct: 0x2f, isImm: false, rs: ^Word(1), rt: ^Word(2), expectRes: Word(1)}, // dsubu t0, s1, s2 + {name: "dsubu. both signed. signed result", funct: 0x2f, isImm: false, rs: ^Word(2), rt: ^Word(1), expectRes: ^Word(0)}, // dsubu t0, s1, s2 + {name: "dsubu. signed and zero", funct: 0x2f, isImm: false, rs: ^Word(0), rt: Word(0), expectRes: ^Word(0)}, // dsubu t0, s1, s2 + {name: "dsubu. overflow", funct: 0x2f, isImm: false, rs: Word(0x80000000_00000000), rt: Word(0x7FFFFFFF_FFFFFFFF), expectRes: Word(0x00000000_00000001)}, // dsubu t0, s1, s2 + + // dsllv + {name: "dsllv", funct: 0x14, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsllv", funct: 0x14, rt: Word(0x20), rs: Word(1), expectRes: Word(0x40)}, + {name: "dsllv sign", funct: 0x14, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0x00_00_00_00_00_00_00_40)}, + {name: "dsllv max", funct: 0x14, rt: Word(0xFF_FF_FF_FF_FF_FF_FF_Fe), rs: Word(0x3f), expectRes: Word(0x0)}, + {name: "dsllv max almost clear", funct: 0x14, rt: Word(0x1), rs: Word(0x3f), expectRes: Word(0x80_00_00_00_00_00_00_00)}, + + // dsrlv t0, s1, s2 + {name: "dsrlv", funct: 0x16, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsrlv", funct: 0x16, rt: Word(0x20), rs: Word(1), expectRes: Word(0x10)}, + {name: "dsrlv sign-extend", funct: 0x16, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0x40_00_00_00_00_00_00_10)}, + {name: "dsrlv max", funct: 0x16, rt: Word(0x7F_FF_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x0)}, + {name: "dsrlv max sign-extend", funct: 0x16, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x1)}, + + // dsrav t0, s1, s2 + {name: "dsrav", funct: 0x17, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsrav", funct: 0x17, rt: Word(0x20), rs: Word(1), expectRes: Word(0x10)}, + {name: "dsrav sign-extend", funct: 0x17, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0xc0_00_00_00_00_00_00_10)}, + {name: "dsrav max", funct: 0x17, rt: Word(0x7F_FF_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x0)}, + {name: "dsrav max sign-extend", funct: 0x17, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FF)}, + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var insn uint32 + var rsReg uint32 = 17 + var rtReg uint32 + var rdReg uint32 + if tt.isImm { + rtReg = 8 + insn = tt.opcode<<26 | rsReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[rsReg] = tt.rs + } else { + rtReg = 18 + rdReg = 8 + insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct + state.GetRegistersRef()[rsReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + } + state.GetMemory().SetUint32(0, insn) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + if tt.isImm { + expected.Registers[rtReg] = tt.expectRes + } else { + expected.Registers[rdReg] = tt.expectRes + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + }) + } +} + +func TestEVMSingleStep_Shift(t *testing.T) { + cases := []struct { + name string + rd Word + rt Word + sa uint32 + funct uint32 + expectRes Word + }{ + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsll t8, s2, 0 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x2)}, // dsll t8, s2, 1 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x80_00_00_00)}, // dsll t8, s2, 31 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FE_00_00_00_00)}, // dsll t8, s2, 1 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll t8, s2, 31 + + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsrl t8, s2, 0 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsrl t8, s2, 1 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0x7F_FF_FF_FF_80_00_00_00)}, // dsrl t8, s2, 1 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0x01_FF_FF_FF_FE)}, // dsrl t8, s2, 31 + + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsra t8, s2, 0 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsra t8, s2, 1 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FF_80_00_00_00)}, // dsra t8, s2, 1 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FE)}, // dsra t8, s2, 31 + + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1_00_00_00_00)}, // dsll32 t8, s2, 0 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x2_00_00_00_00)}, // dsll32 t8, s2, 1 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll32 t8, s2, 31 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0xFF_FF_FF_FE_00_00_00_00)}, // dsll32 t8, s2, 1 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll32 t8, s2, 31 + + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x0)}, // dsrl32 t8, s2, 0 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x0)}, // dsrl32 t8, s2, 31 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0x7F_FF_FF_FF)}, // dsrl32 t8, s2, 1 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x1)}, // dsrl32 t8, s2, 31 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1_0000_0000), sa: 0, expectRes: Word(0x1)}, // dsrl32 t8, s2, 0 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1_0000_0000), sa: 31, expectRes: Word(0x0)}, // dsrl32 t8, s2, 31 + + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x0)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF), sa: 0, expectRes: Word(0x0)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x01_FF_FF_FF_FF), sa: 0, expectRes: Word(0x1)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FF)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_00_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_80)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x0)}, // dsra32 t8, s2, 1 + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var insn uint32 + var rtReg uint32 + var rdReg uint32 + rtReg = 18 + rdReg = 8 + insn = rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct + state.GetRegistersRef()[rdReg] = tt.rd + state.GetRegistersRef()[rtReg] = tt.rt + state.GetMemory().SetUint32(0, insn) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectRes + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + }) + } +} + +func TestEVMSingleStep_LoadStore64(t *testing.T) { + cases := []struct { + name string + rs Word + rt Word + opcode uint32 + memVal Word + expectMemVal Word + expectRes Word + imm uint16 + }{ + {name: "lb 0", opcode: uint32(0x20), memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x71)}, // lb $t0, 0($t1) + {name: "lb 1", opcode: uint32(0x20), imm: 1, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x72)}, // lb $t0, 1($t1) + {name: "lb 2", opcode: uint32(0x20), imm: 2, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x73)}, // lb $t0, 2($t1) + {name: "lb 3", opcode: uint32(0x20), imm: 3, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x74)}, // lb $t0, 3($t1) + {name: "lb 4", opcode: uint32(0x20), imm: 4, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x75)}, // lb $t0, 4($t1) + {name: "lb 5", opcode: uint32(0x20), imm: 5, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x76)}, // lb $t0, 5($t1) + {name: "lb 6", opcode: uint32(0x20), imm: 6, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x77)}, // lb $t0, 6($t1) + {name: "lb 7", opcode: uint32(0x20), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x78)}, // lb $t0, 7($t1) + {name: "lb sign-extended 0", opcode: uint32(0x20), memVal: Word(0x81_72_73_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_81)}, // lb $t0, 0($t1) + {name: "lb sign-extended 1", opcode: uint32(0x20), imm: 1, memVal: Word(0x71_82_73_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_82)}, // lb $t0, 1($t1) + {name: "lb sign-extended 2", opcode: uint32(0x20), imm: 2, memVal: Word(0x71_72_83_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_83)}, // lb $t0, 2($t1) + {name: "lb sign-extended 3", opcode: uint32(0x20), imm: 3, memVal: Word(0x71_72_73_84_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_84)}, // lb $t0, 3($t1) + {name: "lb sign-extended 4", opcode: uint32(0x20), imm: 4, memVal: Word(0x71_72_73_74_85_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_85)}, // lb $t0, 4($t1) + {name: "lb sign-extended 5", opcode: uint32(0x20), imm: 5, memVal: Word(0x71_72_73_74_75_86_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_86)}, // lb $t0, 5($t1) + {name: "lb sign-extended 6", opcode: uint32(0x20), imm: 6, memVal: Word(0x71_72_73_74_75_76_87_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_87)}, // lb $t0, 6($t1) + {name: "lb sign-extended 7", opcode: uint32(0x20), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_88), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_88)}, // lb $t0, 7($t1) + + {name: "lh offset=0", opcode: uint32(0x21), memVal: Word(0x11223344_55667788), expectRes: Word(0x11_22)}, // lhu $t0, 0($t1) + {name: "lh offset=0 sign-extended", opcode: uint32(0x21), memVal: Word(0x81223344_55667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_81_22)}, // lhu $t0, 0($t1) + {name: "lh offset=2", opcode: uint32(0x21), imm: 2, memVal: Word(0x11223344_55667788), expectRes: Word(0x33_44)}, // lhu $t0, 2($t1) + {name: "lh offset=2 sign-extended", opcode: uint32(0x21), imm: 2, memVal: Word(0x11228344_55667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_83_44)}, // lhu $t0, 2($t1) + {name: "lh offset=4", opcode: uint32(0x21), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55_66)}, // lhu $t0, 4($t1) + {name: "lh offset=4 sign-extended", opcode: uint32(0x21), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_85_66)}, // lhu $t0, 4($t1) + {name: "lh offset=6", opcode: uint32(0x21), imm: 6, memVal: Word(0x11223344_55661788), expectRes: Word(0x17_88)}, // lhu $t0, 6($t1) + {name: "lh offset=6 sign-extended", opcode: uint32(0x21), imm: 6, memVal: Word(0x11223344_55668788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_87_88)}, // lhu $t0, 6($t1) + + {name: "lw upper", opcode: uint32(0x23), memVal: Word(0x11223344_55667788), expectRes: Word(0x11223344)}, // lw $t0, 0($t1) + {name: "lw upper sign-extended", opcode: uint32(0x23), memVal: Word(0x81223344_55667788), expectRes: Word(0xFFFFFFFF_81223344)}, // lw $t0, 0($t1) + {name: "lw lower", opcode: uint32(0x23), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55667788)}, // lw $t0, 4($t1) + {name: "lw lower sign-extended", opcode: uint32(0x23), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0xFFFFFFFF_85667788)}, // lw $t0, 4($t1) + + {name: "lbu 0", opcode: uint32(0x24), memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x71)}, // lbu $t0, 0($t1) + {name: "lbu 1", opcode: uint32(0x24), imm: 1, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x72)}, // lbu $t0, 1($t1) + {name: "lbu 2", opcode: uint32(0x24), imm: 2, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x73)}, // lbu $t0, 2($t1) + {name: "lbu 3", opcode: uint32(0x24), imm: 3, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x74)}, // lbu $t0, 3($t1) + {name: "lbu 4", opcode: uint32(0x24), imm: 4, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x75)}, // lbu $t0, 4($t1) + {name: "lbu 5", opcode: uint32(0x24), imm: 5, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x76)}, // lbu $t0, 5($t1) + {name: "lbu 6", opcode: uint32(0x24), imm: 6, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x77)}, // lbu $t0, 6($t1) + {name: "lbu 7", opcode: uint32(0x24), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x78)}, // lbu $t0, 7($t1) + {name: "lbu sign-extended 0", opcode: uint32(0x24), memVal: Word(0x81_72_73_74_75_76_77_78), expectRes: Word(0x81)}, // lbu $t0, 0($t1) + {name: "lbu sign-extended 1", opcode: uint32(0x24), imm: 1, memVal: Word(0x71_82_73_74_75_76_77_78), expectRes: Word(0x82)}, // lbu $t0, 1($t1) + {name: "lbu sign-extended 2", opcode: uint32(0x24), imm: 2, memVal: Word(0x71_72_83_74_75_76_77_78), expectRes: Word(0x83)}, // lbu $t0, 2($t1) + {name: "lbu sign-extended 3", opcode: uint32(0x24), imm: 3, memVal: Word(0x71_72_73_84_75_76_77_78), expectRes: Word(0x84)}, // lbu $t0, 3($t1) + {name: "lbu sign-extended 4", opcode: uint32(0x24), imm: 4, memVal: Word(0x71_72_73_74_85_76_77_78), expectRes: Word(0x85)}, // lbu $t0, 4($t1) + {name: "lbu sign-extended 5", opcode: uint32(0x24), imm: 5, memVal: Word(0x71_72_73_74_75_86_77_78), expectRes: Word(0x86)}, // lbu $t0, 5($t1) + {name: "lbu sign-extended 6", opcode: uint32(0x24), imm: 6, memVal: Word(0x71_72_73_74_75_76_87_78), expectRes: Word(0x87)}, // lbu $t0, 6($t1) + {name: "lbu sign-extended 7", opcode: uint32(0x24), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_88), expectRes: Word(0x88)}, // lbu $t0, 7($t1) + + {name: "lhu offset=0", opcode: uint32(0x25), memVal: Word(0x11223344_55667788), expectRes: Word(0x11_22)}, // lhu $t0, 0($t1) + {name: "lhu offset=0 zero-extended", opcode: uint32(0x25), memVal: Word(0x81223344_55667788), expectRes: Word(0x81_22)}, // lhu $t0, 0($t1) + {name: "lhu offset=2", opcode: uint32(0x25), imm: 2, memVal: Word(0x11223344_55667788), expectRes: Word(0x33_44)}, // lhu $t0, 2($t1) + {name: "lhu offset=2 zero-extended", opcode: uint32(0x25), imm: 2, memVal: Word(0x11228344_55667788), expectRes: Word(0x83_44)}, // lhu $t0, 2($t1) + {name: "lhu offset=4", opcode: uint32(0x25), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55_66)}, // lhu $t0, 4($t1) + {name: "lhu offset=4 zero-extended", opcode: uint32(0x25), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0x85_66)}, // lhu $t0, 4($t1) + {name: "lhu offset=6", opcode: uint32(0x25), imm: 6, memVal: Word(0x11223344_55661788), expectRes: Word(0x17_88)}, // lhu $t0, 6($t1) + {name: "lhu offset=6 zero-extended", opcode: uint32(0x25), imm: 6, memVal: Word(0x11223344_55668788), expectRes: Word(0x87_88)}, // lhu $t0, 6($t1) + + {name: "lwl", opcode: uint32(0x22), rt: Word(0xaa_bb_cc_dd), imm: 4, memVal: Word(0x12_34_56_78), expectRes: Word(0x12_34_56_78)}, // lwl $t0, 4($t1) + {name: "lwl unaligned address", opcode: uint32(0x22), rt: Word(0xaa_bb_cc_dd), imm: 5, memVal: Word(0x12_34_56_78), expectRes: Word(0x34_56_78_dd)}, // lwl $t0, 5($t1) + {name: "lwl offset 0 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_AA_BB_CC_DD)}, // lwl $t0, 0($t1) + {name: "lwl offset 0 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7A_BB_CC_DD)}, // lwl $t0, 0($t1) + {name: "lwl offset 1 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_BB_CC_DD_88)}, // lwl $t0, 1($t1) + {name: "lwl offset 1 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_7B_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7B_CC_DD_88)}, // lwl $t0, 1($t1) + {name: "lwl offset 2 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_CC_DD_77_88)}, // lwl $t0, 2($t1) + {name: "lwl offset 2 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_7C_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7C_DD_77_88)}, // lwl $t0, 2($t1) + {name: "lwl offset 3 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_DD_66_77_88)}, // lwl $t0, 3($t1) + {name: "lwl offset 3 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_7D_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7D_66_77_88)}, // lwl $t0, 3($t1) + {name: "lwl offset 4 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_A1_B1_C1_D1)}, // lwl $t0, 4($t1) + {name: "lwl offset 4 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x00_00_00_00_71_B1_C1_D1)}, // lwl $t0, 4($t1) + {name: "lwl offset 5 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_B1_C1_D1_88)}, // lwl $t0, 5($t1) + {name: "lwl offset 5 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_71_C1_D1), expectRes: Word(0x00_00_00_00_71_C1_D1_88)}, // lwl $t0, 5($t1) + {name: "lwl offset 6 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_C1_D1_77_88)}, // lwl $t0, 6($t1) + {name: "lwl offset 6 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_71_D1), expectRes: Word(0x00_00_00_00_71_D1_77_88)}, // lwl $t0, 6($t1) + {name: "lwl offset 7 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_D1_66_77_88)}, // lwl $t0, 7($t1) + {name: "lwl offset 7 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_71), expectRes: Word(0x00_00_00_00_71_66_77_88)}, // lwl $t0, 7($t1) + + {name: "lwr zero-extended imm 0 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_7A)}, // lwr $t0, 0($t1) + {name: "lwr zero-extended imm 0 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_AA)}, // lwr $t0, 0($t1) + {name: "lwr zero-extended imm 1 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_7A_BB)}, // lwr $t0, 1($t1) + {name: "lwr zero-extended imm 1 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_AA_BB)}, // lwr $t0, 1($t1) + {name: "lwr zero-extended imm 2 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_7A_BB_CC)}, // lwr $t0, 2($t1) + {name: "lwr zero-extended imm 2 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_AA_BB_CC)}, // lwr $t0, 2($t1) + {name: "lwr sign-extended imm 3 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7A_BB_CC_DD)}, // lwr $t0, 3($t1) + {name: "lwr sign-extended imm 3 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_AA_BB_CC_DD)}, // lwr $t0, 3($t1) + {name: "lwr zero-extended imm 4 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_71)}, // lwr $t0, 4($t1) + {name: "lwr zero-extended imm 4 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_66_77_A1)}, // lwr $t0, 4($t1) + {name: "lwr zero-extended imm 5 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_71_B1)}, // lwr $t0, 5($t1) + {name: "lwr zero-extended imm 5 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_66_A1_B1)}, // lwr $t0, 5($t1) + {name: "lwr zero-extended imm 6 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_71_B1_C1)}, // lwr $t0, 6($t1) + {name: "lwr zero-extended imm 6 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_A1_B1_C1)}, // lwr $t0, 6($t1) + {name: "lwr sign-extended imm 7 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x00_00_00_00_71_B1_C1_D1)}, // lwr $t0, 7($t1) + {name: "lwr sign-extended imm 7 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_A1_B1_C1_D1)}, // lwr $t0, 7($t1) + + {name: "sb offset=0", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x88_00_00_00_00_00_00_00)}, // sb $t0, 0($t1) + {name: "sb offset=1", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, expectMemVal: Word(0x00_88_00_00_00_00_00_00)}, // sb $t0, 1($t1) + {name: "sb offset=2", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_88_00_00_00_00_00)}, // sb $t0, 2($t1) + {name: "sb offset=3", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, expectMemVal: Word(0x00_00_00_88_00_00_00_00)}, // sb $t0, 3($t1) + {name: "sb offset=4", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_88_00_00_00)}, // sb $t0, 4($t1) + {name: "sb offset=5", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, expectMemVal: Word(0x00_00_00_00_00_88_00_00)}, // sb $t0, 5($t1) + {name: "sb offset=6", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_88_00)}, // sb $t0, 6($t1) + {name: "sb offset=7", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, expectMemVal: Word(0x00_00_00_00_00_00_00_88)}, // sb $t0, 7($t1) + + {name: "sh offset=0", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x77_88_00_00_00_00_00_00)}, // sh $t0, 0($t1) + {name: "sh offset=2", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_77_88_00_00_00_00)}, // sh $t0, 2($t1) + {name: "sh offset=4", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_77_88_00_00)}, // sh $t0, 4($t1) + {name: "sh offset=6", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_77_88)}, // sh $t0, 6($t1) + + {name: "swl offset=0", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // swl $t0, 0($t1) + {name: "swl offset=1", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 1, expectMemVal: Word(0xAA_55_66_77_A1_B1_C1_D1)}, // swl $t0, 1($t1) + {name: "swl offset=2", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 2, expectMemVal: Word(0xAA_BB_55_66_A1_B1_C1_D1)}, // swl $t0, 2($t1) + {name: "swl offset=3", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 3, expectMemVal: Word(0xAA_BB_CC_55_A1_B1_C1_D1)}, // swl $t0, 3($t1) + {name: "swl offset=4", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // swl $t0, 4($t1) + {name: "swl offset=5", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 5, expectMemVal: Word(0xAA_BB_CC_DD_A1_55_66_77)}, // swl $t0, 5($t1) + {name: "swl offset=6", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_55_66)}, // swl $t0, 6($t1) + {name: "swl offset=7", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 7, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_C1_55)}, // swl $t0, 7($t1) + + {name: "sw offset=0", opcode: uint32(0x2b), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // sw $t0, 0($t1) + {name: "sw offset=4", opcode: uint32(0x2b), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // sw $t0, 4($t1) + + {name: "swr offset=0", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x88_BB_CC_DD_A1_B1_C1_D1)}, // swr $t0, 0($t1) + {name: "swr offset=1", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x77_88_CC_DD_A1_B1_C1_D1)}, // swr $t0, 1($t1) + {name: "swr offset=2", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x66_77_88_DD_A1_B1_C1_D1)}, // swr $t0, 2($t1) + {name: "swr offset=3", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // swr $t0, 3($t1) + {name: "swr offset=4", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_88_B1_C1_D1)}, // swr $t0, 4($t1) + {name: "swr offset=5", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_77_88_C1_D1)}, // swr $t0, 5($t1) + {name: "swr offset=6", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_66_77_88_D1)}, // swr $t0, 6($t1) + {name: "swr offset=7", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // swr $t0, 7($t1) + + // 64-bit instructions + {name: "ldl offset 0 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xAA_BB_CC_DD_A1_B1_C1_D1)}, // ldl $t0, 0($t1) + {name: "ldl offset 1 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xBB_CC_DD_A1_B1_C1_D1_88)}, // ldl $t0, 1($t1) + {name: "ldl offset 2 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xCC_DD_A1_B1_C1_D1_77_88)}, // ldl $t0, 2($t1) + {name: "ldl offset 3 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xDD_A1_B1_C1_D1_66_77_88)}, // ldl $t0, 3($t1) + {name: "ldl offset 4 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xA1_B1_C1_D1_55_66_77_88)}, // ldl $t0, 4($t1) + {name: "ldl offset 5 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xB1_C1_D1_44_55_66_77_88)}, // ldl $t0, 5($t1) + {name: "ldl offset 6 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xC1_D1_33_44_55_66_77_88)}, // ldl $t0, 6($t1) + {name: "ldl offset 7 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xD1_22_33_44_55_66_77_88)}, // ldl $t0, 7($t1) + {name: "ldl offset 0 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7A_BB_CC_DD_A1_B1_C1_D1)}, // ldl $t0, 0($t1) + {name: "ldl offset 1 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_7B_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7B_CC_DD_A1_B1_C1_D1_88)}, // ldl $t0, 1($t1) + {name: "ldl offset 2 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_7C_DD_A1_B1_C1_D1), expectRes: Word(0x7C_DD_A1_B1_C1_D1_77_88)}, // ldl $t0, 2($t1) + {name: "ldl offset 3 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_7D_A1_B1_C1_D1), expectRes: Word(0x7D_A1_B1_C1_D1_66_77_88)}, // ldl $t0, 3($t1) + {name: "ldl offset 4 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x71_B1_C1_D1_55_66_77_88)}, // ldl $t0, 4($t1) + {name: "ldl offset 5 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_71_C1_D1), expectRes: Word(0x71_C1_D1_44_55_66_77_88)}, // ldl $t0, 5($t1) + {name: "ldl offset 6 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_71_D1), expectRes: Word(0x71_D1_33_44_55_66_77_88)}, // ldl $t0, 6($t1) + {name: "ldl offset 7 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_71), expectRes: Word(0x71_22_33_44_55_66_77_88)}, // ldl $t0, 7($t1) + + {name: "ldr offset 0 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_3A)}, // ldr $t0, 0($t1) + {name: "ldr offset 1 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_3A_BB)}, // ldr $t0, 1($t1) + {name: "ldr offset 2 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_3A_BB_CC)}, // ldr $t0, 2($t1) + {name: "ldr offset 3 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_3A_BB_CC_DD)}, // ldr $t0, 3($t1) + {name: "ldr offset 4 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_3A_BB_CC_DD_A1)}, // ldr $t0, 4($t1) + {name: "ldr offset 5 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_3A_BB_CC_DD_A1_B1)}, // ldr $t0, 5($t1) + {name: "ldr offset 6 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_3A_BB_CC_DD_A1_B1_C1)}, // ldr $t0, 6($t1) + {name: "ldr offset 7 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x3A_BB_CC_DD_A1_B1_C1_D1)}, // ldr $t0, 7($t1) + {name: "ldr offset 0 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_AA)}, // ldr $t0, 0($t1) + {name: "ldr offset 1 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_AA_BB)}, // ldr $t0, 1($t1) + {name: "ldr offset 2 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_AA_BB_CC)}, // ldr $t0, 2($t1) + {name: "ldr offset 3 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_AA_BB_CC_DD)}, // ldr $t0, 3($t1) + {name: "ldr offset 4 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_AA_BB_CC_DD_A1)}, // ldr $t0, 4($t1) + {name: "ldr offset 5 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_AA_BB_CC_DD_A1_B1)}, // ldr $t0, 5($t1) + {name: "ldr offset 6 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_AA_BB_CC_DD_A1_B1_C1)}, // ldr $t0, 6($t1) + {name: "ldr offset 7 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xAA_BB_CC_DD_A1_B1_C1_D1)}, // ldr $t0, 7($t1) + + {name: "lwu upper", opcode: uint32(0x27), memVal: Word(0x11223344_55667788), expectRes: Word(0x11223344)}, // lw $t0, 0($t1) + {name: "lwu upper sign", opcode: uint32(0x27), memVal: Word(0x81223344_55667788), expectRes: Word(0x81223344)}, // lw $t0, 0($t1) + {name: "lwu lower", opcode: uint32(0x27), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55667788)}, // lw $t0, 4($t1) + {name: "lwu lower sign", opcode: uint32(0x27), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0x85667788)}, // lw $t0, 4($t1) + + {name: "sdl offset=0", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sdl $t0, 0($t1) + {name: "sdl offset=1", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 1, expectMemVal: Word(0xAA_11_22_33_44_55_66_77)}, // sdl $t0, 1($t1) + {name: "sdl offset=2", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 2, expectMemVal: Word(0xAA_BB_11_22_33_44_55_66)}, // sdl $t0, 2($t1) + {name: "sdl offset=3", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 3, expectMemVal: Word(0xAA_BB_CC_11_22_33_44_55)}, // sdl $t0, 3($t1) + {name: "sdl offset=4", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_11_22_33_44)}, // sdl $t0, 4($t1) + {name: "sdl offset=5", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 5, expectMemVal: Word(0xAA_BB_CC_DD_A1_11_22_33)}, // sdl $t0, 5($t1) + {name: "sdl offset=6", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_11_22)}, // sdl $t0, 6($t1) + {name: "sdl offset=7", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 7, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_C1_11)}, // sdl $t0, 7($t1) + + {name: "sdr offset=0", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x88_BB_CC_DD_A1_B1_C1_D1)}, // sdr $t0, 0($t1) + {name: "sdr offset=1", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x77_88_CC_DD_A1_B1_C1_D1)}, // sdr $t0, 1($t1) + {name: "sdr offset=2", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x66_77_88_DD_A1_B1_C1_D1)}, // sdr $t0, 2($t1) + {name: "sdr offset=3", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // sdr $t0, 3($t1) + {name: "sdr offset=4", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x44_55_66_77_88_B1_C1_D1)}, // sdr $t0, 4($t1) + {name: "sdr offset=5", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x33_44_55_66_77_88_C1_D1)}, // sdr $t0, 5($t1) + {name: "sdr offset=6", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x22_33_44_55_66_77_88_D1)}, // sdr $t0, 6($t1) + {name: "sdr offset=7", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sdr $t0, 7($t1) + + {name: "ld", opcode: uint32(0x37), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7A_BB_CC_DD_A1_B1_C1_D1)}, // ld $t0, 0($t1) + {name: "ld signed", opcode: uint32(0x37), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0x8A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x8A_BB_CC_DD_A1_B1_C1_D1)}, // ld $t0, 0($t1) + + {name: "sd", opcode: uint32(0x3f), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sd $t0, 0($t1) + {name: "sd signed", opcode: uint32(0x3f), rt: Word(0x81_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x81_22_33_44_55_66_77_88)}, // sd $t0, 4($t1) + } + + v := GetMultiThreadedTestCase(t) + var t1 Word = 0xFF000000_00000108 + var baseReg uint32 = 9 + var rtReg uint32 = 8 + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + effAddr := arch.AddressMask & t1 + + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + + insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[baseReg] = t1 + + state.GetMemory().SetUint32(0, insn) + state.GetMemory().SetWord(t1&arch.AddressMask, tt.memVal) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + if tt.expectMemVal != 0 { + expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal) + } else { + expected.Registers[rtReg] = tt.expectRes + } + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + }) + } +} + +func TestEVMSingleStep_DivMult(t *testing.T) { + cases := []struct { + name string + rs Word + rt Word + funct uint32 + expectLo Word + expectHi Word + expectPanic string + }{ + // dmult s1, s2 + // expected hi,lo were verified using qemu-mips + {name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, + {name: "dmult 1", funct: 0x1c, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "dmult 2", funct: 0x1c, rs: 0x01_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00, expectHi: 0}, + {name: "dmult 3", funct: 0x1c, rs: 0x01_00_00_00_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00_00_00_00, expectHi: 0}, + {name: "dmult 4", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_00, rt: 2, expectLo: 0x80_00_00_00_00_00_00_00, expectHi: 0x0}, + {name: "dmult 5", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x4_00}, + {name: "dmult 6", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0xFF_FF_FF_FF_FF_FF_F8_00}, + {name: "dmult 7", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_00, rt: 0x80_00_00_00_00_00_00_00, expectLo: 0x0, expectHi: 0x40_00_00_00_00_00_00_00}, + {name: "dmult 8", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_01, rt: 0x1000, expectLo: 0x1000, expectHi: 0x4_00}, + {name: "dmult 9", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_80, rt: 0x80_00_00_00_00_00_00_80, expectLo: 0x4000, expectHi: 0x3F_FF_FF_FF_FF_FF_FF_80}, + {name: "dmult 10", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x1), expectLo: 0xFF_FF_FF_FF_FF_FF_FF_FF, expectHi: 0xFF_FF_FF_FF_FF_FF_FF_FF}, + {name: "dmult 11", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: Word(0)}, + {name: "dmult 12", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E0), expectLo: 0xFC_FC_FD_0A_8E_20_EB_A0, expectHi: 0x00_00_00_00_00_00_00_0E}, + {name: "dmult 13", funct: 0x1c, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E1), expectLo: 0xD5_44_33_22_5E_2E_3E_1F, expectHi: 0xD5_5D_E6_6E_D0_E8_E0_F0}, + {name: "dmult 14", funct: 0x1c, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x8F_FF_FF_FF_FF_FF_FF_FF), expectLo: 0xF0_00_00_00_00_00_00_01, expectHi: 0xC7_FF_FF_FF_FF_FF_FF_FF}, + + // dmultu s1, s2 + {name: "dmultu 0", funct: 0x1d, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, + {name: "dmultu 1", funct: 0x1d, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "dmultu 2", funct: 0x1d, rs: 0x01_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00, expectHi: 0}, + {name: "dmultu 3", funct: 0x1d, rs: 0x01_00_00_00_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00_00_00_00, expectHi: 0}, + {name: "dmultu 4", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_00, rt: 2, expectLo: 0x80_00_00_00_00_00_00_00, expectHi: 0x0}, + {name: "dmultu 5", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x4_00}, + {name: "dmultu 6", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x8_00}, + {name: "dmultu 7", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_00, rt: 0x80_00_00_00_00_00_00_00, expectLo: 0x0, expectHi: 0x40_00_00_00_00_00_00_00}, + {name: "dmultu 8", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_01, rt: 0x1000, expectLo: 0x1000, expectHi: 0x4_00}, + {name: "dmultu 9", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_80, rt: 0x80_00_00_00_00_00_00_80, expectLo: 0x4000, expectHi: 0x40_00_00_00_00_00_00_80}, + {name: "dmultu 10", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: Word(0xFF_FF_FF_FF_FF_FF_FF_FE)}, + {name: "dmultu 11", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: 0xFF_FF_FF_FF_FF_FF_FF_FE}, + {name: "dmultu 12", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E0), expectLo: 0xFC_FC_FD_0A_8E_20_EB_A0, expectHi: 0xAA_BB_CC_DD_A1_D1_C1_C1}, + {name: "dmultu 13", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E1), expectLo: 0xD5_44_33_22_5E_2E_3E_1F, expectHi: 0x55_5D_E6_6E_D0_E8_E0_EF}, + {name: "dmultu 14", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x8F_FF_FF_FF_FF_FF_FF_FF), expectLo: 0xF0_00_00_00_00_00_00_01, expectHi: 0x47_FF_FF_FF_FF_FF_FF_FE}, + + // ddiv rs, rt + {name: "ddiv", funct: 0x1e, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, + {name: "ddiv", funct: 0x1e, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x3F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0xFF_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: ^Word(0), rt: 2, expectLo: 0, expectHi: ^Word(0)}, + {name: "ddiv", funct: 0x1e, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0x80_00_00_01_00_00_00_00, expectHi: 0}, + + // ddivu + {name: "ddivu", funct: 0x1f, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, + {name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x3F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x7F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: 2, expectLo: 0x7F_FF_FF_FF_FF_FF_FF_FF, expectHi: 1}, + {name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0, expectHi: 0x7F_FF_FF_FF_00_00_00_00}, + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var rsReg uint32 = 17 + var rtReg uint32 = 18 + insn := rsReg<<21 | rtReg<<16 | tt.funct + state.GetRegistersRef()[rsReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + state.GetMemory().SetUint32(0, insn) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.LO = tt.expectLo + expected.HI = tt.expectHi + + if tt.expectPanic != "" { + require.PanicsWithValue(t, tt.expectPanic, func() { _, _ = goVm.Step(true) }) + // TODO(#12250): Assert EVM panic for divide by zero + // testutil.AssertEVMReverts(t, state, contracts, nil, proofData, errMsg) + } else { + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + } + }) + } +} + +func TestEVMSingleStepBranch64(t *testing.T) { + var tracer *tracing.Hooks + + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + pc Word + expectNextPC Word + opcode uint32 + regimm uint32 + expectLink bool + rs arch.SignedInteger + rt Word + offset uint16 + }{ + // blez + {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "blez zero rs", pc: 0x10, opcode: 0x6, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign rs", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "blez rs only sign bit set", pc: 0x10, opcode: 0x6, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign-extended offset", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + + // bgtz + {name: "bgtz", pc: 0, opcode: 0x7, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgtz sign-extended offset", pc: 0x10, opcode: 0x7, rs: 0x5, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bgtz large rs", pc: 0x10, opcode: 0x7, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgtz zero rs", pc: 0x10, opcode: 0x7, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz sign rs", pc: 0x10, opcode: 0x7, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz rs only sign bit set", pc: 0x10, opcode: 0x7, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x18}, + + // bltz t0, $x + {name: "bltz", pc: 0, opcode: 0x1, regimm: 0x0, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "bltz large rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz zero rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz sign rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "bltz rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgez t0, $x + {name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez zero rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgez sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bgez large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14}, + {name: "bgez fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgezal t0, $x + {name: "bgezal", pc: 0, opcode: 0x1, regimm: 0x11, rs: 0x5, offset: 0x100, expectNextPC: 0x404, expectLink: true}, + {name: "bgezal large rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x0, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: -1, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bgezal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14, expectLink: true}, + {name: "bgezal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14, expectLink: true}, + {name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true}, + } + + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) + state := goVm.GetState() + const rsReg = 8 // t0 + insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) + state.GetMemory().SetUint32(tt.pc, insn) + state.GetRegistersRef()[rsReg] = Word(tt.rs) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.Step += 1 + expected.PC = state.GetCpu().NextPC + expected.NextPC = tt.expectNextPC + if tt.expectLink { + expected.Registers[31] = state.GetPC() + 8 + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + }) + } + } +} diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index c9dfd2f09634..f1e08e6eda36 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -991,3 +991,94 @@ func TestEntryEVM(t *testing.T) { }) } } + +func TestEVMSingleStepBranch(t *testing.T) { + var tracer *tracing.Hooks + + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + pc Word + expectNextPC Word + opcode uint32 + regimm uint32 + expectLink bool + rs arch.SignedInteger + rt Word + offset uint16 + }{ + // blez + {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "blez zero rs", pc: 0x10, opcode: 0x6, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign rs", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "blez rs only sign bit set", pc: 0x10, opcode: 0x6, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign-extended offset", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + + // bgtz + {name: "bgtz", pc: 0, opcode: 0x7, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgtz sign-extended offset", pc: 0x10, opcode: 0x7, rs: 0x5, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bgtz large rs", pc: 0x10, opcode: 0x7, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgtz zero rs", pc: 0x10, opcode: 0x7, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz sign rs", pc: 0x10, opcode: 0x7, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz rs only sign bit set", pc: 0x10, opcode: 0x7, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x18}, + + // bltz t0, $x + {name: "bltz", pc: 0, opcode: 0x1, regimm: 0x0, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "bltz large rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz zero rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz sign rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "bltz rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgez t0, $x + {name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez zero rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgez sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bgez large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14}, + {name: "bgez fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgezal t0, $x + {name: "bgezal", pc: 0, opcode: 0x1, regimm: 0x11, rs: 0x5, offset: 0x100, expectNextPC: 0x404, expectLink: true}, + {name: "bgezal large rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x0, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: -1, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bgezal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14, expectLink: true}, + {name: "bgezal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14, expectLink: true}, + {name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true}, + } + + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) + state := goVm.GetState() + const rsReg = 8 // t0 + insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) + state.GetMemory().SetUint32(tt.pc, insn) + state.GetRegistersRef()[rsReg] = Word(tt.rs) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.Step += 1 + expected.PC = state.GetCpu().NextPC + expected.NextPC = tt.expectNextPC + if tt.expectLink { + expected.Registers[31] = state.GetPC() + 8 + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + }) + } + } +} diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 6c297ea946d0..0ea9142a9168 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -1011,7 +1011,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) { goVm, state, contracts := setup(t, 2101, nil) mttestutil.InitializeSingleThread(2101+i, state, i%2 == 1) effAddr := c.timespecAddr & arch.AddressMask - effAddr2 := effAddr + 4 + effAddr2 := effAddr + arch.WordSizeBytes step := state.Step // Define LL-related params @@ -1121,6 +1121,8 @@ var NoopSyscalls = map[string]uint32{ "SysLlseek": 4140, "SysMinCore": 4217, "SysTgkill": 4266, + "SysGetRLimit": 4076, + "SysLseek": 4019, "SysMunmap": 4091, "SysSetITimer": 4104, "SysTimerCreate": 4257, diff --git a/cannon/mipsevm/testutil/arch.go b/cannon/mipsevm/testutil/arch.go index 2efa75eafcec..a69896c041e5 100644 --- a/cannon/mipsevm/testutil/arch.go +++ b/cannon/mipsevm/testutil/arch.go @@ -40,3 +40,9 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui actual := mem.GetWord(effAddr) require.Equal(t, Word(value), actual) } + +// ToSignedInteger converts the unsigend Word to a SignedInteger. +// Useful for avoiding Go compiler warnings for literals that don't fit in a signed type +func ToSignedInteger(x Word) arch.SignedInteger { + return arch.SignedInteger(x) +} diff --git a/cannon/mipsevm/testutil/elf.go b/cannon/mipsevm/testutil/elf.go index b5b63cdeb1d3..3e1896963cda 100644 --- a/cannon/mipsevm/testutil/elf.go +++ b/cannon/mipsevm/testutil/elf.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" ) @@ -26,3 +27,12 @@ func LoadELFProgram[T mipsevm.FPVMState](t require.TestingT, name string, initSt require.NoError(t, program.PatchStack(state), "add initial stack") return state, meta } + +// ProgramPath returns the appropriate ELF test program for the current architecture +func ProgramPath(programName string) string { + basename := programName + ".elf" + if !arch.IsMips32 { + basename = programName + ".64.elf" + } + return "../../testdata/example/bin/" + basename +} diff --git a/cannon/mipsevm/testutil/oracle.go b/cannon/mipsevm/testutil/oracle.go index 64b0f31a897c..b5c4f7dce915 100644 --- a/cannon/mipsevm/testutil/oracle.go +++ b/cannon/mipsevm/testutil/oracle.go @@ -67,7 +67,7 @@ func StaticPrecompileOracle(t *testing.T, precompile common.Address, requiredGas } func ClaimTestOracle(t *testing.T) (po mipsevm.PreimageOracle, stdOut string, stdErr string) { - s := uint64(1000) + s := uint64(0x00FFFFFF_00001000) a := uint64(3) b := uint64(4) diff --git a/cannon/mipsevm/testutil/vmtests.go b/cannon/mipsevm/testutil/vmtests.go index 382c60833399..333720a4ec02 100644 --- a/cannon/mipsevm/testutil/vmtests.go +++ b/cannon/mipsevm/testutil/vmtests.go @@ -16,10 +16,14 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" ) -type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM +type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM type StateFactory[T mipsevm.FPVMState] func() T func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFactory[T], vmFactory VMFactory[T], excludedTests ...string) { + if !arch.IsMips32 { + // TODO: guard these tests by the cannon32 build tag + t.Skip("Open MIPS tests are not appropriate for cannon64") + } testFiles, err := os.ReadDir("../tests/open_mips_tests/test/bin") require.NoError(t, err) @@ -52,7 +56,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa // set the return address ($ra) to jump into when test completes state.GetRegistersRef()[31] = EndAddr - us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger()) + us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger(), nil) // Catch panics and check if they are expected defer func() { @@ -94,12 +98,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa } func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { - state, _ := LoadELFProgram(t, "../../testdata/example/bin/hello.elf", initState, doPatchGo) + state, meta := LoadELFProgram(t, ProgramPath("hello"), initState, doPatchGo) var stdOutBuf, stdErrBuf bytes.Buffer - us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) + us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta) - for i := 0; i < 400_000; i++ { + maxSteps := 430_000 + for i := 0; i < maxSteps; i++ { if us.GetState().GetExited() { break } @@ -107,7 +112,7 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create require.NoError(t, err) } - require.True(t, state.GetExited(), "must complete program") + require.Truef(t, state.GetExited(), "must complete program. reached %d of max %d steps", state.GetStep(), maxSteps) require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0") require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello") @@ -115,12 +120,12 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create } func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { - state, _ := LoadELFProgram(t, "../../testdata/example/bin/claim.elf", initState, doPatchGo) + state, meta := LoadELFProgram(t, ProgramPath("claim"), initState, doPatchGo) oracle, expectedStdOut, expectedStdErr := ClaimTestOracle(t) var stdOutBuf, stdErrBuf bytes.Buffer - us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) + us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta) for i := 0; i < 2000_000; i++ { if us.GetState().GetExited() { diff --git a/cannon/testdata/example/Makefile b/cannon/testdata/example/Makefile index 7b3b8fdf019d..5b0dee96eb9a 100644 --- a/cannon/testdata/example/Makefile +++ b/cannon/testdata/example/Makefile @@ -1,7 +1,13 @@ all: elf +.PHONY: elf32 +elf32: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod)) + +.PHONY: elf64 +elf64: $(patsubst %/go.mod,bin/%.64.elf,$(wildcard */go.mod)) + .PHONY: elf -elf: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod)) +elf: elf32 elf64 .PHONY: dump dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) @@ -9,6 +15,9 @@ dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) bin: mkdir bin +bin/%.64.elf: bin + cd $(@:bin/%.64.elf=%) && GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ . + # take any directory with a go mod, and build an ELF # verify output with: readelf -h bin/.elf # result is mips32, big endian, R3000 diff --git a/cannon/testdata/example/alloc/go.mod b/cannon/testdata/example/alloc/go.mod index a5dd16ce8b27..c364668acc10 100644 --- a/cannon/testdata/example/alloc/go.mod +++ b/cannon/testdata/example/alloc/go.mod @@ -1,6 +1,6 @@ module alloc -go 1.22 +go 1.22.0 toolchain go1.22.7 diff --git a/cannon/testdata/example/claim/go.mod b/cannon/testdata/example/claim/go.mod index dd76391de26b..e3904911a172 100644 --- a/cannon/testdata/example/claim/go.mod +++ b/cannon/testdata/example/claim/go.mod @@ -1,6 +1,6 @@ module claim -go 1.22 +go 1.22.0 toolchain go1.22.7 diff --git a/cannon/testdata/example/hello/go.mod b/cannon/testdata/example/hello/go.mod index b54bb78c6aee..f036b5dc97fc 100644 --- a/cannon/testdata/example/hello/go.mod +++ b/cannon/testdata/example/hello/go.mod @@ -1,5 +1,5 @@ module hello -go 1.22 +go 1.22.0 -toolchain go1.22.0 +toolchain go1.22.7 diff --git a/go.mod b/go.mod index 7478f4a18528..ab9e5725ce87 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum-optimism/optimism -go 1.22 +go 1.22.0 toolchain go1.22.7 @@ -47,11 +47,10 @@ require ( github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.5 golang.org/x/crypto v0.28.0 - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c golang.org/x/sync v0.8.0 golang.org/x/term v0.25.0 golang.org/x/time v0.7.0 - lukechampine.com/uint128 v1.3.0 ) require ( @@ -95,7 +94,7 @@ require ( github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/felixge/fgprof v0.9.3 // indirect + github.com/felixge/fgprof v0.9.5 // indirect github.com/ferranbt/fastssz v0.1.2 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -114,7 +113,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect @@ -234,11 +233,11 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.20.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.26.0 // indirect google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect diff --git a/go.sum b/go.sum index 7b74fd565d0f..de095722c8b5 100644 --- a/go.sum +++ b/go.sum @@ -89,12 +89,18 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= @@ -192,8 +198,9 @@ github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= +github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -242,6 +249,9 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -299,8 +309,9 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA= +github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -363,6 +374,7 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= @@ -394,6 +406,7 @@ github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZl github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -443,6 +456,7 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= @@ -473,6 +487,7 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -593,6 +608,7 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -846,8 +862,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -860,8 +876,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1021,8 +1037,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1094,8 +1110,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 45944697945e..5b62da033bf0 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -140,12 +140,12 @@ "sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784" }, "src/cannon/MIPS.sol": { - "initCodeHash": "0x8fb590d45fa06fdc7ae55860827d6b1abf070c9dc5e18cd28176e844fa1da6c9", - "sourceCodeHash": "0x01d3d59020ec29ce78f68f977da5b7754455743121bda65626509864ab6c9da9" + "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2", + "sourceCodeHash": "0x3c2e5d6d39b29a60dcd1a776363518c87fcfef9a8d80e38696f56b6eec5bd53a" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0x88acf3297642c2c9e0c1e92b48f59f7015915f25fb816cac44ee0073a1c93ccb", - "sourceCodeHash": "0x3dd89839f268569cbf2659beb42e3ac3c53887472cfc94a6e339d2b3a963b941" + "initCodeHash": "0x5c292882075bd06e68b89119be9096040bc913f51bb878ce4a9dfdd674330dd5", + "sourceCodeHash": "0x17c7b0edb9d20932eaf1b038e3e05e457f0461d2c8691ba1940fb4c2b0dfd123" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 337a2d47ee2b..38989cfc7b7b 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -44,8 +44,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.4 - string public constant version = "1.2.1-beta.4"; + /// @custom:semver 1.2.1-beta.5 + string public constant version = "1.2.1-beta.5"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 7946cd0c3362..3f5044f14e04 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.17 - string public constant version = "1.0.0-beta.17"; + /// @custom:semver 1.0.0-beta.18 + string public constant version = "1.0.0-beta.18"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -593,6 +593,10 @@ contract MIPS2 is ISemver { // ignored } else if (syscall_no == sys.SYS_TIMERDELETE) { // ignored + } else if (syscall_no == sys.SYS_GETRLIMIT) { + // ignored + } else if (syscall_no == sys.SYS_LSEEK) { + // ignored } else { if (syscall_no == sys.SYS_FSTAT64 || syscall_no == sys.SYS_STAT64 || syscall_no == sys.SYS_LLSEEK) { // noop diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol index a2402531c82d..eab4a61db65a 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol @@ -7,6 +7,7 @@ import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; library MIPSInstructions { uint32 internal constant OP_LOAD_LINKED = 0x30; uint32 internal constant OP_STORE_CONDITIONAL = 0x38; + uint32 internal constant REG_RA = 31; struct CoreStepLogicParams { /// @param opcode The opcode value parsed from insn_. @@ -501,6 +502,11 @@ library MIPSInstructions { if (rtv == 1) { shouldBranch = int32(_rs) >= 0; } + // bgezal (i.e. bal mnemonic) + if (rtv == 0x11) { + shouldBranch = int32(_rs) >= 0; + _registers[REG_RA] = _cpu.pc + 8; // always set regardless of branch taken + } } // Update the state's previous PC diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol index 9b5278e052ae..4354163a9d3f 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol @@ -71,6 +71,8 @@ library MIPSSyscalls { uint32 internal constant SYS_LLSEEK = 4140; uint32 internal constant SYS_MINCORE = 4217; uint32 internal constant SYS_TGKILL = 4266; + uint32 internal constant SYS_GETRLIMIT = 4076; + uint32 internal constant SYS_LSEEK = 4019; // profiling-related syscalls - ignored uint32 internal constant SYS_SETITIMER = 4104; From 3159e0eb4ba2934455d9fa3be0ca0b6a07c4c1d6 Mon Sep 17 00:00:00 2001 From: Gutflo <107882881+klein818@users.noreply.github.com> Date: Thu, 24 Oct 2024 00:47:06 +0200 Subject: [PATCH 009/451] fix: fix potential error when running without any argument (#11812) --- packages/contracts-bedrock/scripts/checks/check-snapshots.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/scripts/checks/check-snapshots.sh b/packages/contracts-bedrock/scripts/checks/check-snapshots.sh index 66a04dc331d4..18557eba52a5 100755 --- a/packages/contracts-bedrock/scripts/checks/check-snapshots.sh +++ b/packages/contracts-bedrock/scripts/checks/check-snapshots.sh @@ -3,7 +3,7 @@ set -euo pipefail # Check for the --no-build flag # Generate snapshots -if [ "$1" == "--no-build" ]; then +if [ "${1:-}" == "--no-build" ]; then just snapshots-no-build else just snapshots From 433f488f8e5456cd1f56cf878eb3884e6fff3b16 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Thu, 24 Oct 2024 06:40:03 +0700 Subject: [PATCH 010/451] fix(ci): apply return arg fmt rule to tests (#12546) Applies the semgrep rule for return-arg-fmt to Solidity tests. --- .../test/L1/ResourceMetering.t.sol | 4 ++-- .../contracts-bedrock/test/L2/L2Genesis.t.sol | 2 +- .../contracts-bedrock/test/cannon/MIPS.t.sol | 20 +++++++++---------- .../contracts-bedrock/test/cannon/MIPS2.t.sol | 12 +++++------ .../handlers/Protocol.t.sol | 8 ++++---- .../helpers/HandlerGetters.t.sol | 12 +++++------ .../MockL2ToL2CrossDomainMessenger.t.sol | 4 ++-- .../test/mocks/TestERC1271Wallet.sol | 12 +++++++++-- semgrep/sol-rules.yaml | 2 +- 9 files changed, 42 insertions(+), 34 deletions(-) diff --git a/packages/contracts-bedrock/test/L1/ResourceMetering.t.sol b/packages/contracts-bedrock/test/L1/ResourceMetering.t.sol index 6d01cdb30867..18f5ba82283f 100644 --- a/packages/contracts-bedrock/test/L1/ResourceMetering.t.sol +++ b/packages/contracts-bedrock/test/L1/ResourceMetering.t.sol @@ -374,8 +374,8 @@ contract ArtifactResourceMetering_Test is Test { // Call the metering code and catch the various // types of errors. uint256 gasConsumed = 0; - try meter.use{ gas: 30_000_000 }(requestedGas) returns (uint256 _gasConsumed) { - gasConsumed = _gasConsumed; + try meter.use{ gas: 30_000_000 }(requestedGas) returns (uint256 gasConsumed_) { + gasConsumed = gasConsumed_; } catch (bytes memory err) { bytes32 hash = keccak256(err); if (hash == cannotBuyMoreGas) { diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 68fc374178ee..0f5ad085a822 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -170,7 +170,7 @@ contract L2GenesisTest is Test { } /// @notice Creates mock L1Dependencies for testing purposes. - function _dummyL1Deps() internal pure returns (L1Dependencies memory _deps) { + function _dummyL1Deps() internal pure returns (L1Dependencies memory deps_) { return L1Dependencies({ l1CrossDomainMessengerProxy: payable(address(0x100000)), l1StandardBridgeProxy: payable(address(0x100001)), diff --git a/packages/contracts-bedrock/test/cannon/MIPS.t.sol b/packages/contracts-bedrock/test/cannon/MIPS.t.sol index 21710ef1e18e..e4221dfcf66b 100644 --- a/packages/contracts-bedrock/test/cannon/MIPS.t.sol +++ b/packages/contracts-bedrock/test/cannon/MIPS.t.sol @@ -1801,22 +1801,22 @@ contract MIPS_Test is CommonTest { uint32 val ) internal - returns (IMIPS.State memory state, bytes memory proof) + returns (IMIPS.State memory state_, bytes memory proof_) { - (state.memRoot, proof) = ffi.getCannonMemoryProof(pc, insn, addr, val); - state.pc = pc; - state.nextPC = pc + 4; + (state_.memRoot, proof_) = ffi.getCannonMemoryProof(pc, insn, addr, val); + state_.pc = pc; + state_.nextPC = pc + 4; } - function encodeitype(uint8 opcode, uint8 rs, uint8 rt, uint16 imm) internal pure returns (uint32 insn) { - insn = uint32(opcode) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | imm; + function encodeitype(uint8 opcode, uint8 rs, uint8 rt, uint16 imm) internal pure returns (uint32 insn_) { + insn_ = uint32(opcode) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | imm; } - function encodespec(uint8 rs, uint8 rt, uint8 rd, uint16 funct) internal pure returns (uint32 insn) { - insn = uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); + function encodespec(uint8 rs, uint8 rt, uint8 rd, uint16 funct) internal pure returns (uint32 insn_) { + insn_ = uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); } - function encodespec2(uint8 rs, uint8 rt, uint8 rd, uint8 funct) internal pure returns (uint32 insn) { - insn = uint32(28) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); + function encodespec2(uint8 rs, uint8 rt, uint8 rd, uint8 funct) internal pure returns (uint32 insn_) { + insn_ = uint32(28) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); } } diff --git a/packages/contracts-bedrock/test/cannon/MIPS2.t.sol b/packages/contracts-bedrock/test/cannon/MIPS2.t.sol index 22eb49b4ef0e..0c2ad04362bb 100644 --- a/packages/contracts-bedrock/test/cannon/MIPS2.t.sol +++ b/packages/contracts-bedrock/test/cannon/MIPS2.t.sol @@ -2832,16 +2832,16 @@ contract MIPS2_Test is CommonTest { } } - function encodeitype(uint8 opcode, uint8 rs, uint8 rt, uint16 imm) internal pure returns (uint32 insn) { - insn = uint32(opcode) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | imm; + function encodeitype(uint8 opcode, uint8 rs, uint8 rt, uint16 imm) internal pure returns (uint32 insn_) { + insn_ = uint32(opcode) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | imm; } - function encodespec(uint8 rs, uint8 rt, uint8 rd, uint16 funct) internal pure returns (uint32 insn) { - insn = uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); + function encodespec(uint8 rs, uint8 rt, uint8 rd, uint16 funct) internal pure returns (uint32 insn_) { + insn_ = uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); } - function encodespec2(uint8 rs, uint8 rt, uint8 rd, uint8 funct) internal pure returns (uint32 insn) { - insn = uint32(28) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); + function encodespec2(uint8 rs, uint8 rt, uint8 rd, uint8 funct) internal pure returns (uint32 insn_) { + insn_ = uint32(28) << 26 | uint32(rs) << 21 | uint32(rt) << 16 | uint32(rd) << 11 | uint32(funct); } } diff --git a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol index d4a12c4067bd..770be029bbbe 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol @@ -151,7 +151,7 @@ contract ProtocolHandler is TestBase, StdUtils, Actors { uint256 chainId ) internal - returns (OptimismSuperchainERC20 supertoken) + returns (OptimismSuperchainERC20 supertoken_) { // this salt would be used in production. Tokens sharing it will be bridgable with each other bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); @@ -162,7 +162,7 @@ contract ProtocolHandler is TestBase, StdUtils, Actors { // what we use in the tests to walk around two contracts needing two different addresses // tbf we could be using CREATE1, but this feels more verbose bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); - supertoken = OptimismSuperchainERC20( + supertoken_ = OptimismSuperchainERC20( address( // TODO: Use the OptimismSuperchainERC20 Beacon Proxy new ERC1967Proxy{ salt: hackySalt }( @@ -171,7 +171,7 @@ contract ProtocolHandler is TestBase, StdUtils, Actors { ) ) ); - MESSENGER.registerSupertoken(realSalt, chainId, address(supertoken)); - allSuperTokens.push(address(supertoken)); + MESSENGER.registerSupertoken(realSalt, chainId, address(supertoken_)); + allSuperTokens.push(address(supertoken_)); } } diff --git a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/HandlerGetters.t.sol b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/HandlerGetters.t.sol index b081aa48c6bb..d072dc92165b 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/HandlerGetters.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/HandlerGetters.t.sol @@ -7,16 +7,16 @@ import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableM contract HandlerGetters is ProtocolHandler { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; - function deploySaltsLength() external view returns (uint256 length) { + function deploySaltsLength() external view returns (uint256 length_) { return ghost_totalSupplyAcrossChains.length(); } - function totalSupplyAcrossChainsAtIndex(uint256 index) external view returns (bytes32 salt, uint256 supply) { - return ghost_totalSupplyAcrossChains.at(index); + function totalSupplyAcrossChainsAtIndex(uint256 _index) external view returns (bytes32 salt_, uint256 supply_) { + return ghost_totalSupplyAcrossChains.at(_index); } - function tokensInTransitForDeploySalt(bytes32 salt) external view returns (uint256 amount) { - (, amount) = ghost_tokensInTransit.tryGet(salt); - return amount; + function tokensInTransitForDeploySalt(bytes32 _salt) external view returns (uint256 amount_) { + (, amount_) = ghost_tokensInTransit.tryGet(_salt); + return amount_; } } diff --git a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol index b5d70596ed63..2d4a57c46c3c 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol @@ -112,7 +112,7 @@ contract MockL2ToL2CrossDomainMessenger { // Internal helpers // //////////////////////// - function _decodePayload(bytes calldata payload) internal pure returns (address recipient, uint256 amount) { - (, recipient, amount) = abi.decode(payload[4:], (address, address, uint256)); + function _decodePayload(bytes calldata payload) internal pure returns (address recipient_, uint256 amount_) { + (, recipient_, amount_) = abi.decode(payload[4:], (address, address, uint256)); } } diff --git a/packages/contracts-bedrock/test/mocks/TestERC1271Wallet.sol b/packages/contracts-bedrock/test/mocks/TestERC1271Wallet.sol index c04ed608b7d7..6e2ef47f67e6 100644 --- a/packages/contracts-bedrock/test/mocks/TestERC1271Wallet.sol +++ b/packages/contracts-bedrock/test/mocks/TestERC1271Wallet.sol @@ -13,7 +13,15 @@ contract TestERC1271Wallet is Ownable, IERC1271 { transferOwnership(originalOwner); } - function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { - return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); + function isValidSignature( + bytes32 _hash, + bytes memory _signature + ) + public + view + override + returns (bytes4 magicValue_) + { + return ECDSA.recover(_hash, _signature) == owner() ? this.isValidSignature.selector : bytes4(0); } } diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 3c7422c25ed3..7074fe2daff0 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -65,7 +65,7 @@ rules: paths: exclude: - op-chain-ops/script/testdata/scripts/ScriptExample.s.sol - - packages/contracts-bedrock/test + - packages/contracts-bedrock/test/safe-tools - packages/contracts-bedrock/scripts/libraries/Solarray.sol - packages/contracts-bedrock/scripts/interfaces/IGnosisSafe.sol - packages/contracts-bedrock/src/dispute/interfaces/IPermissionedDisputeGame.sol From 280bac53d5cec211923efd8dae3848559ec6c65c Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Thu, 24 Oct 2024 09:52:09 +1000 Subject: [PATCH 011/451] op-dispute-mon: Fix flaky test that was dependent on map ordering (#12606) --- op-dispute-mon/mon/extract/extractor_test.go | 28 +++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/op-dispute-mon/mon/extract/extractor_test.go b/op-dispute-mon/mon/extract/extractor_test.go index ee4b8a63ee25..cc0605714a04 100644 --- a/op-dispute-mon/mon/extract/extractor_test.go +++ b/op-dispute-mon/mon/extract/extractor_test.go @@ -155,7 +155,12 @@ func TestExtractor_Extract(t *testing.T) { }) t.Run("UseCachedValueOnFailure", func(t *testing.T) { - enricher := &mockEnricher{} + enricher := &mockEnricher{ + action: func(game *monTypes.EnrichedGameData) error { + game.Status = gameTypes.GameStatusDefenderWon + return nil + }, + } extractor, _, games, _, cl := setupExtractorTest(t, enricher) gameA := common.Address{0xaa} gameB := common.Address{0xbb} @@ -169,8 +174,10 @@ func TestExtractor_Extract(t *testing.T) { require.Len(t, enriched, 2) require.Equal(t, 2, enricher.calls) firstUpdateTime := cl.Now() - require.Equal(t, firstUpdateTime, enriched[0].LastUpdateTime) - require.Equal(t, firstUpdateTime, enriched[1].LastUpdateTime) + // All results should have current LastUpdateTime + for _, data := range enriched { + require.Equal(t, firstUpdateTime, data.LastUpdateTime) + } cl.AdvanceTime(2 * time.Minute) secondUpdateTime := cl.Now() @@ -189,10 +196,17 @@ func TestExtractor_Extract(t *testing.T) { require.Equal(t, 1, failed) require.Len(t, enriched, 2) require.Equal(t, 4, enricher.calls) - require.Equal(t, enriched[0].Status, gameTypes.GameStatusInProgress) // Uses cached value from game A - require.Equal(t, enriched[1].Status, gameTypes.GameStatusChallengerWon) // Updates game B - require.Equal(t, firstUpdateTime, enriched[0].LastUpdateTime) - require.Equal(t, secondUpdateTime, enriched[1].LastUpdateTime) + // The returned games are not in a fixed order, create a map to look up the game we need to assert + actual := make(map[common.Address]*monTypes.EnrichedGameData) + for _, data := range enriched { + actual[data.Proxy] = data + } + require.Contains(t, actual, gameA) + require.Contains(t, actual, gameB) + require.Equal(t, actual[gameA].Status, gameTypes.GameStatusDefenderWon) // Uses cached value from game A + require.Equal(t, actual[gameB].Status, gameTypes.GameStatusChallengerWon) // Updates game B + require.Equal(t, firstUpdateTime, actual[gameA].LastUpdateTime) + require.Equal(t, secondUpdateTime, actual[gameB].LastUpdateTime) }) } From 03e0b881aa3aceccbbbadaaafe8b804f16542241 Mon Sep 17 00:00:00 2001 From: rickck11 Date: Wed, 23 Oct 2024 21:02:55 -0300 Subject: [PATCH 012/451] test-kontrol: optimize the doc (#12065) * optimize the doc * small fix * reomve outdated content --- packages/contracts-bedrock/test/kontrol/README.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/test/kontrol/README.md b/packages/contracts-bedrock/test/kontrol/README.md index 25660756963c..7e4dc5a2809a 100644 --- a/packages/contracts-bedrock/test/kontrol/README.md +++ b/packages/contracts-bedrock/test/kontrol/README.md @@ -115,12 +115,6 @@ The `runKontrolDeployment` function of [`KontrolDeployment`](./deployment/Kontro Once new deployment steps have been added to `runKontrolDeployment` the state-diff files have to [be rebuilt](#build-deployment-summary). -#### Include existing tests on the new state-diff recorded bytecode - -The next step is to include tests for the newly included state updates in [`DeploymentSummary.t.sol`](deployment/DeploymentSummary.t.sol). These tests inherit the tests from [`test`](../L1) of the contracts deployed by `runKontrolDeployment`. This ensures that deployment steps were implemented correctly and that the state updates are correct. - -It might be necessary to set some of the existing tests from [`test`](../L1) as virtual because they can't be executed as is. See [`DeploymentSummary.t.sol`](deployment/DeploymentSummary.t.sol) for more concrete examples. - #### Write the proof Write your proof in a `.k.sol` file in the [`proofs`](./proofs/) folder, which is the `test` directory used by the `kprove` profile to run the proofs (see [Deployment Summary Process](#deployment-summary-process)). The name of the new proofs should start with `prove` (or `check`) instead of `test` to avoid `forge test` running them. The reason for this is that if Kontrol cheatcodes (see [Kontrol's own cheatcodes](https://github.com/runtimeverification/kontrol-cheatcodes/blob/master/src/KontrolCheats.sol)) are used in a test, it will not be runnable by `forge`. Currently, none of the tests are using custom Kontrol cheatcodes, but this is something to bear in mind. @@ -143,8 +137,7 @@ function setUp() public { superchainConfig = SuperchainConfig(superchainConfigProxyAddress); } ``` - -Note that the names of the addresses come from [`DeploymentSummary.t.sol`](deployment/DeploymentSummary.t.sol) and are automatically generated by the [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) script. +Note that the names of the addresses are automatically generated by the [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) script. #### Add your test to [`run-kontrol.sh`](./scripts/run-kontrol.sh) @@ -155,7 +148,6 @@ As described in [Execute Proofs](#execute-proofs), there's a `script` mode for s ### Assumptions 1. A critical invariant of the `KontrolDeployment.sol` contract is that it stays in sync with the original `Deploy.s.sol` contract. - Currently, this is partly enforced by running some of the standard post-`setUp` deployment assertions in `DeploymentSummary.t.sol`. A more rigorous approach may be to leverage the `ChainAssertions` library, but more investigation is required to determine if this is feasible without large changes to the deploy script. 2. Size of `bytes[]` arguments. In [`OptimismPortal.k.sol`](./proofs/OptimismPortal.k.sol), the `prove_proveWithdrawalTransaction_paused` proof is broken down into 11 different proofs, each corresponding to a different size of the `_withdrawalProof` argument, which is of type `bytes[]`. We execute the same logic for lengths of `_withdrawalProof` ranging from 0 to 10, setting the length of each symbolic `bytes` element to 600. From 532a1464181ea7cc954d04d40e609dd73d270fcb Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 23 Oct 2024 20:33:47 -0400 Subject: [PATCH 013/451] chore(op-service): Add missing fields to blob API types (#12602) * inclusion proof * fix test * add inclusion proof * goimports --- op-e2e/e2eutils/fakebeacon/blobs.go | 1 + op-service/eth/blobs_api.go | 9 +++++---- op-service/eth/blobs_api_test.go | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/op-e2e/e2eutils/fakebeacon/blobs.go b/op-e2e/e2eutils/fakebeacon/blobs.go index a96042bc7add..4ec516926742 100644 --- a/op-e2e/e2eutils/fakebeacon/blobs.go +++ b/op-e2e/e2eutils/fakebeacon/blobs.go @@ -127,6 +127,7 @@ func (f *FakeBeacon) Start(addr string) error { Slot: eth.Uint64String(slot), }, }, + InclusionProof: make([]eth.Bytes32, 0), } copy(sidecars[i].Blob[:], bundle.Blobs[ix]) } diff --git a/op-service/eth/blobs_api.go b/op-service/eth/blobs_api.go index 3dbff1745bc2..239f97528ede 100644 --- a/op-service/eth/blobs_api.go +++ b/op-service/eth/blobs_api.go @@ -1,5 +1,7 @@ package eth +import "github.com/ethereum/go-ethereum/common/hexutil" + type BlobSidecar struct { Blob Blob `json:"blob"` Index Uint64String `json:"index"` @@ -13,8 +15,7 @@ type APIBlobSidecar struct { KZGCommitment Bytes48 `json:"kzg_commitment"` KZGProof Bytes48 `json:"kzg_proof"` SignedBlockHeader SignedBeaconBlockHeader `json:"signed_block_header"` - // The inclusion-proof of the blob-sidecar into the beacon-block is ignored, - // since we verify blobs by their versioned hashes against the execution-layer block instead. + InclusionProof []Bytes32 `json:"kzg_commitment_inclusion_proof"` } func (sc *APIBlobSidecar) BlobSidecar() *BlobSidecar { @@ -27,8 +28,8 @@ func (sc *APIBlobSidecar) BlobSidecar() *BlobSidecar { } type SignedBeaconBlockHeader struct { - Message BeaconBlockHeader `json:"message"` - // signature is ignored, since we verify blobs against EL versioned-hashes + Message BeaconBlockHeader `json:"message"` + Signature hexutil.Bytes `json:"signature"` } type BeaconBlockHeader struct { diff --git a/op-service/eth/blobs_api_test.go b/op-service/eth/blobs_api_test.go index 7c585cb7ac99..babad4a50537 100644 --- a/op-service/eth/blobs_api_test.go +++ b/op-service/eth/blobs_api_test.go @@ -82,17 +82,19 @@ func TestAPIGetBlobSidecarsResponse(t *testing.T) { var resp eth.APIGetBlobSidecarsResponse require.NoError(json.Unmarshal(jsonStr, &resp)) require.NotEmpty(resp.Data) - require.Equal(5, reflect.TypeOf(*resp.Data[0]).NumField(), "APIBlobSidecar changed, adjust test") - require.Equal(1, reflect.TypeOf(resp.Data[0].SignedBlockHeader).NumField(), "SignedBeaconBlockHeader changed, adjust test") + require.Equal(6, reflect.TypeOf(*resp.Data[0]).NumField(), "APIBlobSidecar changed, adjust test") + require.Equal(2, reflect.TypeOf(resp.Data[0].SignedBlockHeader).NumField(), "SignedBeaconBlockHeader changed, adjust test") require.Equal(5, reflect.TypeOf(resp.Data[0].SignedBlockHeader.Message).NumField(), "BeaconBlockHeader changed, adjust test") require.NotZero(resp.Data[0].Blob) require.NotZero(resp.Data[1].Index) require.NotZero(resp.Data[0].KZGCommitment) require.NotZero(resp.Data[0].KZGProof) + require.NotZero(resp.Data[0].InclusionProof) require.NotZero(resp.Data[0].SignedBlockHeader.Message.Slot) require.NotZero(resp.Data[0].SignedBlockHeader.Message.ParentRoot) require.NotZero(resp.Data[0].SignedBlockHeader.Message.BodyRoot) require.NotZero(resp.Data[0].SignedBlockHeader.Message.ProposerIndex) require.NotZero(resp.Data[0].SignedBlockHeader.Message.StateRoot) + require.NotZero(resp.Data[0].SignedBlockHeader.Signature) } From 6ec241be4d0b9da4ffa1bd0c56079d9b998532b1 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Thu, 24 Oct 2024 10:53:23 +0700 Subject: [PATCH 014/451] maint(ci): bump heavy fuzz runs (#12605) Bumps the number of heavy fuzz runs to 20k. 10k recently missed a flake so bumping to 20k to try to avoid that in the future. Not perfect but it will reduce the probability a bit. Developers can still manually set the number of fuzz runs for a particular test with annotations if 20k is too high. --- packages/contracts-bedrock/foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index b173f71abc33..59b16410f3a4 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -76,7 +76,7 @@ runs = 256 depth = 32 [profile.ciheavy] -fuzz = { runs = 10000 } +fuzz = { runs = 20000 } [profile.ciheavy.invariant] runs = 128 From 898383f1a6d1ce44e06da15fa029971303fdf92f Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Wed, 23 Oct 2024 21:55:49 -0600 Subject: [PATCH 015/451] op-e2e: Attempt fix for alt-da test flakiness (#12603) This test fails a lot when executors are under load. This PR changes the test to find the first block with more than one batcher transaction in it, which should be sufficient to assert that the batcher is submitting multiple transactions at once. --- op-e2e/system/altda/concurrent_test.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/op-e2e/system/altda/concurrent_test.go b/op-e2e/system/altda/concurrent_test.go index 4d6835b1fc55..a2ad1f8bcbb8 100644 --- a/op-e2e/system/altda/concurrent_test.go +++ b/op-e2e/system/altda/concurrent_test.go @@ -55,23 +55,20 @@ func TestBatcherConcurrentAltDARequests(t *testing.T) { err = driver.StartBatchSubmitting() require.NoError(t, err) - totalBatcherTxsCount := int64(0) - // wait for up to 5 L1 blocks, expecting 10 L2 batcher txs in them. - // usually only 3 is required, but it's possible additional L1 blocks will be created - // before the batcher starts, so we wait additional blocks. - for i := int64(0); i < 5; i++ { - block, err := geth.WaitForBlock(big.NewInt(int64(startingL1BlockNum)+i), l1Client, time.Duration(cfg.DeployConfig.L1BlockTime*2)*time.Second) + // Iterate over up to 10 blocks. The number of transactions sent by the batcher should + // exceed the number of blocks. + checkBlocks := 10 + for i := 0; i < checkBlocks; i++ { + block, err := geth.WaitForBlock(big.NewInt(int64(startingL1BlockNum)+int64(i)), l1Client, time.Duration(cfg.DeployConfig.L1BlockTime*2)*time.Second) require.NoError(t, err, "Waiting for l1 blocks") // there are possibly other services (proposer/challenger) in the background sending txs // so we only count the batcher txs batcherTxCount, err := transactions.TransactionsBySender(block, cfg.DeployConfig.BatchSenderAddress) require.NoError(t, err) - totalBatcherTxsCount += int64(batcherTxCount) - - if totalBatcherTxsCount >= numL1TxsExpected { + if batcherTxCount > 1 { return } } - t.Fatal("Expected at least 10 transactions from the batcher") + t.Fatalf("did not find more than 1 batcher tx per block in %d blocks", checkBlocks) } From 5f2cc843fde7960cb2bc907542b528260a3f5f6f Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Thu, 24 Oct 2024 14:41:19 +1000 Subject: [PATCH 016/451] op-challenger: Add some info on run-trace to the README (#12607) --- op-challenger/README.md | 46 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/op-challenger/README.md b/op-challenger/README.md index 69420e419cfa..3274dc47d808 100644 --- a/op-challenger/README.md +++ b/op-challenger/README.md @@ -23,6 +23,7 @@ accessed by running `./op-challenger --help`. To run `op-challenger` against the local devnet, first clean and run the devnet from the root of the repository. + ```shell make devnet-clean make devnet-up @@ -31,6 +32,7 @@ make devnet-up Then build the `op-challenger` with `make op-challenger`. Run the `op-challenger` with: + ```shell DISPUTE_GAME_FACTORY=$(jq -r .DisputeGameFactoryProxy .devnet/addresses.json) ./op-challenger/bin/op-challenger \ @@ -85,6 +87,9 @@ in the L2 output oracle. Optionally, you may specify the game type (aka "trace type") using the `--trace-type` flag, which is set to the cannon trace type by default. +For known networks, the `--game-factory-address` option can be replaced by `--network`. See the `--help` output for a +list of predefined networks. + ### move The `move` subcommand can be run with either the `--attack` or `--defend` flag, @@ -154,7 +159,7 @@ If the game is resolved successfully, the result is printed. ```shell ./bin/op-challenger list-games \ --l1-eth-rpc \ - --network + --game-factory-address ``` Prints the games created by the game factory along with their current status. @@ -162,6 +167,9 @@ Prints the games created by the game factory along with their current status. * `L1_ETH_RPC` - the RPC endpoint of the L1 endpoint to use (e.g. `http://localhost:8545`). * `GAME_FACTORY_ADDRESS` - the address of the dispute game factory contract on L1. +For known networks, the `--game-factory-address` option can be replaced by `--network`. See the `--help` output for a +list of predefined networks. + ### list-claims ```shell @@ -174,3 +182,39 @@ Prints the list of current claims in a dispute game. * `L1_ETH_RPC` - the RPC endpoint of the L1 endpoint to use (e.g. `http://localhost:8545`). * `GAME_ADDRESS` - the address of the dispute game to list the move in. + +### run-trace + +```shell +./bin/op-challenger run-trace \ + --network \ + --l1-eth-rpc= \ + --l1-beacon= \ + --l2-eth-rpc= \ + --rollup-rpc= \ + --data-dir= \ + --prestates-url= \ + --run= +``` + +* `PREDEFINED_NETWORK` - the name of a predefined L2 network. +* `L1_ETH_RPC` - the RPC endpoint of the L1 endpoint to use (e.g. `http://localhost:8545`). +* `L1_BEACON` - the REST endpoint of the L1 beacon node to use (e.g. `http://localhost:5100`). +* `L2_ETH_RPC` - the RPC endpoint of the L2 execution client to use +* `ROLLUP_RPC` - the RPC endpoint of the L2 consensus client to use +* `DATA_DIR` - the directory to use to store data +* `PRESTATES_URL` - the base URL to download required prestates from +* `RUN_CONFIG` - the trace providers and prestates to run. e.g. `cannon,asterisc-kona/kona-0.1.0-alpha.5/0x03c50fbef46a05f93ea7665fa89015c2108e10c1b4501799c0663774bd35a9c5` + +Testing utility that continuously runs the specified trace providers against real chain data. The trace providers can be +configured with multiple different prestates. This allows testing both the current and potential future prestates with +the fault proofs virtual machine used by the trace provider. + +The same CLI options as `op-challenger` itself are supported to configure the trace providers. The additional `--run` +option allows specifying which prestates to use. The format is `traceType/name/prestateHash` where traceType is the +trace type to use with the prestate (e.g cannon or asterisc-kona), name is an arbitrary name for the prestate to use +when reporting metrics and prestateHash is the hex encoded absolute prestate commitment to use. If name is omitted the +trace type name is used.If the prestateHash is omitted, the absolute prestate hash used for new games on-chain. + +For example to run both the production cannon prestate and a custom +prestate, use `--run cannon,cannon/next-prestate/0x03c1f0d45248190f80430a4c31e24f8108f05f80ff8b16ecb82d20df6b1b43f3`. From c23daa008fa7a9757c484cdddcd29d75f18ca298 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Thu, 24 Oct 2024 14:51:54 +0700 Subject: [PATCH 017/451] maint(ci): apply expectRevert rule to tests (#12543) Updates semgrep config to apply expectRevert to tests. Fixes a few instances where this wasn't being followed. --- packages/contracts-bedrock/test/L2/ETHLiquidity.t.sol | 2 +- packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol | 2 +- .../contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol | 2 +- semgrep/sol-rules.yaml | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/test/L2/ETHLiquidity.t.sol b/packages/contracts-bedrock/test/L2/ETHLiquidity.t.sol index d9d2d92a8928..9814e776b2e5 100644 --- a/packages/contracts-bedrock/test/L2/ETHLiquidity.t.sol +++ b/packages/contracts-bedrock/test/L2/ETHLiquidity.t.sol @@ -105,7 +105,7 @@ contract ETHLiquidity_Test is CommonTest { uint256 amount = STARTING_LIQUIDITY_BALANCE + 1; // Act - vm.expectRevert(); + vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args ethLiquidity.mint(amount); // Assert diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index aa8eaab09939..4710274baac5 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -314,7 +314,7 @@ contract SuperchainWETH_Test is CommonTest { superchainWeth.deposit{ value: _amount }(); // Act - vm.expectRevert(); + vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args superchainWeth.crosschainBurn(_from, _amount + 1); } diff --git a/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol b/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol index 7b5c7c6357b2..781ada343335 100644 --- a/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol +++ b/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol @@ -351,7 +351,7 @@ contract MerkleTrie_get_Test is Test { // Ambiguous revert check- all that we care is that it *does* fail. This case may // fail within different branches. - vm.expectRevert(); + vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args MerkleTrie.get(key, proof, root); } diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 7074fe2daff0..81b128f229b2 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -37,7 +37,7 @@ rules: - pattern: vm.expectRevert() paths: exclude: - - packages/contracts-bedrock/test + - packages/contracts-bedrock/test/dispute/WETH98.t.sol - id: sol-style-input-arg-fmt languages: [solidity] @@ -80,7 +80,6 @@ rules: exclude: - packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol - - id: sol-style-malformed-require languages: [solidity] severity: ERROR From 196613d916bbb866f87391b43bc76cb2259abd4a Mon Sep 17 00:00:00 2001 From: Disco <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 24 Oct 2024 05:01:40 -0300 Subject: [PATCH 018/451] feat: support permit2 on superchainweth (#12596) * feat: support permit2 on superchainweth * chore: run pre-pr --------- Co-authored-by: agusduha Co-authored-by: gotzenx <78360669+gotzenx@users.noreply.github.com> --- packages/contracts-bedrock/semver-lock.json | 12 +-- .../snapshots/abi/DelayedWETH.json | 6 +- .../snapshots/abi/SuperchainWETH.json | 6 +- .../contracts-bedrock/snapshots/abi/WETH.json | 6 +- .../snapshots/abi/WETH98.json | 6 +- .../snapshots/storageLayout/DelayedWETH.json | 4 +- .../storageLayout/SuperchainWETH.json | 4 +- .../snapshots/storageLayout/WETH.json | 4 +- .../snapshots/storageLayout/WETH98.json | 4 +- .../src/L2/SuperchainWETH.sol | 15 +++- packages/contracts-bedrock/src/L2/WETH.sol | 4 +- .../src/dispute/DelayedWETH.sol | 6 +- .../src/universal/WETH98.sol | 35 +++++--- .../test/L2/SuperchainWETH.t.sol | 80 +++++++++++++++++++ 14 files changed, 145 insertions(+), 47 deletions(-) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 5b62da033bf0..26828dd40f8e 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -132,12 +132,12 @@ "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0xc120d1fd9edd8aa392cf112859a3a3907c3b5d55132d6a5edd80ff1d6b6baeaa", - "sourceCodeHash": "0x445f088aa91fafde43f558f34da4b9f85b82e6a76f8b2dec9563838eb4335928" + "initCodeHash": "0xc72cb486b815a65daa8bd5d0af8c965b6708cf8caf03de0a18023a63a6e6c604", + "sourceCodeHash": "0x39fff1d4702a2fec3dcba29c7f9604eabf20d32e9c5bf4377edeb620624aa467" }, "src/L2/WETH.sol": { - "initCodeHash": "0xfb253765520690623f177941c2cd9eba23e4c6d15063bccdd5e98081329d8956", - "sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784" + "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", + "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, "src/cannon/MIPS.sol": { "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2", @@ -156,8 +156,8 @@ "sourceCodeHash": "0x1d918a536d9f6c900efdf069e96c2a27bb49340d6d1ebaa92dd6b481835a9a82" }, "src/dispute/DelayedWETH.sol": { - "initCodeHash": "0x835b322de7d5c84b415e99f2cb1000411df18995b5476f2116ac6f897f2d0910", - "sourceCodeHash": "0xdbd64724b73f8f9d6f1cc72bb662a99b9955ab72950a8f6ffeb1d2454997f60b" + "initCodeHash": "0xb31e0ff80fd69bc3f3b7d53f3fa42da4cdae393e41b8816719ce5ebe3d248688", + "sourceCodeHash": "0x1dfc68560c0805faa78360e3d4ef2d768e2f3d6c0c7183d2077a2c4277c778db" }, "src/dispute/DisputeGameFactory.sol": { "initCodeHash": "0xd72eced7cb5400d93188038a707fe6c1b04077f059cd8e2f5253e871de2cee3b", diff --git a/packages/contracts-bedrock/snapshots/abi/DelayedWETH.json b/packages/contracts-bedrock/snapshots/abi/DelayedWETH.json index a84394e6b003..a6f0dd660468 100644 --- a/packages/contracts-bedrock/snapshots/abi/DelayedWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/DelayedWETH.json @@ -22,12 +22,12 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "owner", "type": "address" }, { "internalType": "address", - "name": "", + "name": "spender", "type": "address" } ], @@ -70,7 +70,7 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "src", "type": "address" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json index d76246faf218..fe53a4cbfb5e 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json @@ -11,12 +11,12 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "owner", "type": "address" }, { "internalType": "address", - "name": "", + "name": "spender", "type": "address" } ], @@ -59,7 +59,7 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "src", "type": "address" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/WETH.json b/packages/contracts-bedrock/snapshots/abi/WETH.json index 9430e99a4c85..03cf2880ccce 100644 --- a/packages/contracts-bedrock/snapshots/abi/WETH.json +++ b/packages/contracts-bedrock/snapshots/abi/WETH.json @@ -11,12 +11,12 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "owner", "type": "address" }, { "internalType": "address", - "name": "", + "name": "spender", "type": "address" } ], @@ -59,7 +59,7 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "src", "type": "address" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/WETH98.json b/packages/contracts-bedrock/snapshots/abi/WETH98.json index 364d6b0591dc..019b5c37a1af 100644 --- a/packages/contracts-bedrock/snapshots/abi/WETH98.json +++ b/packages/contracts-bedrock/snapshots/abi/WETH98.json @@ -11,12 +11,12 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "owner", "type": "address" }, { "internalType": "address", - "name": "", + "name": "spender", "type": "address" } ], @@ -59,7 +59,7 @@ "inputs": [ { "internalType": "address", - "name": "", + "name": "src", "type": "address" } ], diff --git a/packages/contracts-bedrock/snapshots/storageLayout/DelayedWETH.json b/packages/contracts-bedrock/snapshots/storageLayout/DelayedWETH.json index 4e8b771cd9bb..6fdbd4718b69 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/DelayedWETH.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/DelayedWETH.json @@ -36,14 +36,14 @@ }, { "bytes": "32", - "label": "balanceOf", + "label": "_balanceOf", "offset": 0, "slot": "101", "type": "mapping(address => uint256)" }, { "bytes": "32", - "label": "allowance", + "label": "_allowance", "offset": 0, "slot": "102", "type": "mapping(address => mapping(address => uint256))" diff --git a/packages/contracts-bedrock/snapshots/storageLayout/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/storageLayout/SuperchainWETH.json index ac5f38a75a04..93eac8f28429 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/SuperchainWETH.json @@ -1,14 +1,14 @@ [ { "bytes": "32", - "label": "balanceOf", + "label": "_balanceOf", "offset": 0, "slot": "0", "type": "mapping(address => uint256)" }, { "bytes": "32", - "label": "allowance", + "label": "_allowance", "offset": 0, "slot": "1", "type": "mapping(address => mapping(address => uint256))" diff --git a/packages/contracts-bedrock/snapshots/storageLayout/WETH.json b/packages/contracts-bedrock/snapshots/storageLayout/WETH.json index ac5f38a75a04..93eac8f28429 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/WETH.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/WETH.json @@ -1,14 +1,14 @@ [ { "bytes": "32", - "label": "balanceOf", + "label": "_balanceOf", "offset": 0, "slot": "0", "type": "mapping(address => uint256)" }, { "bytes": "32", - "label": "allowance", + "label": "_allowance", "offset": 0, "slot": "1", "type": "mapping(address => mapping(address => uint256))" diff --git a/packages/contracts-bedrock/snapshots/storageLayout/WETH98.json b/packages/contracts-bedrock/snapshots/storageLayout/WETH98.json index ac5f38a75a04..93eac8f28429 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/WETH98.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/WETH98.json @@ -1,14 +1,14 @@ [ { "bytes": "32", - "label": "balanceOf", + "label": "_balanceOf", "offset": 0, "slot": "0", "type": "mapping(address => uint256)" }, { "bytes": "32", - "label": "allowance", + "label": "_allowance", "offset": 0, "slot": "1", "type": "mapping(address => mapping(address => uint256))" diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index a9242d9d0ab3..e03b689ba150 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -6,6 +6,7 @@ import { WETH98 } from "src/universal/WETH98.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -22,8 +23,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// do not use a custom gas token. contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.8 - string public constant version = "1.0.0-beta.8"; + /// @custom:semver 1.0.0-beta.9 + string public constant version = "1.0.0-beta.9"; /// @inheritdoc WETH98 function deposit() public payable override { @@ -37,11 +38,17 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { super.withdraw(_amount); } + /// @inheritdoc WETH98 + function allowance(address owner, address spender) public view override returns (uint256) { + if (spender == Preinstalls.Permit2) return type(uint256).max; + return super.allowance(owner, spender); + } + /// @notice Mints WETH to an address. /// @param _to The address to mint WETH to. /// @param _amount The amount of WETH to mint. function _mint(address _to, uint256 _amount) internal { - balanceOf[_to] += _amount; + _balanceOf[_to] += _amount; emit Transfer(address(0), _to, _amount); } @@ -49,7 +56,7 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { /// @param _from The address to burn WETH from. /// @param _amount The amount of WETH to burn. function _burn(address _from, uint256 _amount) internal { - balanceOf[_from] -= _amount; + _balanceOf[_from] -= _amount; emit Transfer(_from, address(0), _amount); } diff --git a/packages/contracts-bedrock/src/L2/WETH.sol b/packages/contracts-bedrock/src/L2/WETH.sol index fb24f0747338..dacd62c36de9 100644 --- a/packages/contracts-bedrock/src/L2/WETH.sol +++ b/packages/contracts-bedrock/src/L2/WETH.sol @@ -14,8 +14,8 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// @title WETH contract that reads the name and symbol from the L1Block contract. /// Allows for nice rendering of token names for chains using custom gas token. contract WETH is WETH98, ISemver { - /// @custom:semver 1.1.0-beta.2 - string public constant version = "1.1.0-beta.2"; + /// @custom:semver 1.1.0-beta.3 + string public constant version = "1.1.0-beta.3"; /// @notice Returns the name of the wrapped native asset. Will be "Wrapped Ether" /// if the native asset is Ether. diff --git a/packages/contracts-bedrock/src/dispute/DelayedWETH.sol b/packages/contracts-bedrock/src/dispute/DelayedWETH.sol index 42b6022d12fe..3438c22e3679 100644 --- a/packages/contracts-bedrock/src/dispute/DelayedWETH.sol +++ b/packages/contracts-bedrock/src/dispute/DelayedWETH.sol @@ -32,8 +32,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { event Unwrap(address indexed src, uint256 wad); /// @notice Semantic version. - /// @custom:semver 1.2.0-beta.2 - string public constant version = "1.2.0-beta.2"; + /// @custom:semver 1.2.0-beta.3 + string public constant version = "1.2.0-beta.3"; /// @notice Returns a withdrawal request for the given address. mapping(address => mapping(address => WithdrawalRequest)) public withdrawals; @@ -112,7 +112,7 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { /// @param _wad The amount of WETH to recover. function hold(address _guy, uint256 _wad) external { require(msg.sender == owner(), "DelayedWETH: not owner"); - allowance[_guy][msg.sender] = _wad; + _allowance[_guy][msg.sender] = _wad; emit Approval(_guy, msg.sender, _wad); } } diff --git a/packages/contracts-bedrock/src/universal/WETH98.sol b/packages/contracts-bedrock/src/universal/WETH98.sol index 5665e5f9210f..e211db22b473 100644 --- a/packages/contracts-bedrock/src/universal/WETH98.sol +++ b/packages/contracts-bedrock/src/universal/WETH98.sol @@ -26,8 +26,8 @@ import { IWETH } from "src/universal/interfaces/IWETH.sol"; contract WETH98 is IWETH { uint8 public constant decimals = 18; - mapping(address => uint256) public balanceOf; - mapping(address => mapping(address => uint256)) public allowance; + mapping(address => uint256) internal _balanceOf; + mapping(address => mapping(address => uint256)) internal _allowance; /// @notice Pipes to deposit. receive() external payable { @@ -49,16 +49,26 @@ contract WETH98 is IWETH { return "WETH"; } + /// @inheritdoc IWETH + function allowance(address owner, address spender) public view virtual returns (uint256) { + return _allowance[owner][spender]; + } + + /// @inheritdoc IWETH + function balanceOf(address src) public view returns (uint256) { + return _balanceOf[src]; + } + /// @inheritdoc IWETH function deposit() public payable virtual { - balanceOf[msg.sender] += msg.value; + _balanceOf[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } /// @inheritdoc IWETH function withdraw(uint256 wad) public virtual { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; + require(_balanceOf[msg.sender] >= wad); + _balanceOf[msg.sender] -= wad; payable(msg.sender).transfer(wad); emit Withdrawal(msg.sender, wad); } @@ -70,7 +80,7 @@ contract WETH98 is IWETH { /// @inheritdoc IWETH function approve(address guy, uint256 wad) external returns (bool) { - allowance[msg.sender][guy] = wad; + _allowance[msg.sender][guy] = wad; emit Approval(msg.sender, guy, wad); return true; } @@ -82,15 +92,16 @@ contract WETH98 is IWETH { /// @inheritdoc IWETH function transferFrom(address src, address dst, uint256 wad) public returns (bool) { - require(balanceOf[src] >= wad); + require(_balanceOf[src] >= wad); - if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; + uint256 senderAllowance = allowance(src, msg.sender); + if (src != msg.sender && senderAllowance != type(uint256).max) { + require(senderAllowance >= wad); + _allowance[src][msg.sender] -= wad; } - balanceOf[src] -= wad; - balanceOf[dst] += wad; + _balanceOf[src] -= wad; + _balanceOf[dst] += wad; emit Transfer(src, dst, wad); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index 4710274baac5..b428dc4cd191 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -7,6 +7,7 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; +import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Interfaces import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; @@ -373,4 +374,83 @@ contract SuperchainWETH_Test is CommonTest { // Assert assertFalse(success); } + + /// @notice Tests that the allowance function returns the max uint256 value when the spender is Permit. + /// @param _randomCaller The address that will call the function - used to fuzz better since the behaviour should be + /// the same regardless of the caller. + /// @param _src The funds owner. + function testFuzz_allowance_fromPermit2_succeeds(address _randomCaller, address _src) public { + vm.prank(_randomCaller); + uint256 _allowance = superchainWeth.allowance(_src, Preinstalls.Permit2); + + assertEq(_allowance, type(uint256).max); + } + + /// @notice Tests that the allowance function returns the correct allowance when the spender is not Permit. + /// @param _randomCaller The address that will call the function - used to fuzz better + /// since the behaviour should be the same regardless of the caller. + /// @param _src The funds owner. + /// @param _guy The address of the spender - It cannot be Permit2. + function testFuzz_allowance_succeeds(address _randomCaller, address _src, address _guy, uint256 _wad) public { + // Assume + vm.assume(_guy != Preinstalls.Permit2); + + // Arrange + vm.prank(_src); + superchainWeth.approve(_guy, _wad); + + // Act + vm.prank(_randomCaller); + uint256 _allowance = superchainWeth.allowance(_src, _guy); + + // Assert + assertEq(_allowance, _wad); + } + + /// @notice Tests that `transferFrom` works when the caller (spender) is Permit2, without any explicit approval. + /// @param _src The funds owner. + /// @param _dst The address of the recipient. + /// @param _wad The amount of WETH to transfer. + function testFuzz_transferFrom_whenPermit2IsCaller_succeeds(address _src, address _dst, uint256 _wad) public { + vm.assume(_src != _dst); + + // Arrange + deal(address(superchainWeth), _src, _wad); + + vm.expectEmit(address(superchainWeth)); + emit Transfer(_src, _dst, _wad); + + // Act + vm.prank(Preinstalls.Permit2); + superchainWeth.transferFrom(_src, _dst, _wad); + + // Assert + assertEq(superchainWeth.balanceOf(_src), 0); + assertEq(superchainWeth.balanceOf(_dst), _wad); + } + + /// @notice Tests that `transferFrom` works when the caller (spender) is Permit2, and `_src` equals `_dst` without + /// an explicit approval. + /// The balance should remain the same on this scenario. + /// @param _user The source and destination address. + /// @param _wad The amount of WETH to transfer. + function testFuzz_transferFrom_whenPermit2IsCallerAndSourceIsDestination_succeeds( + address _user, + uint256 _wad + ) + public + { + // Arrange + deal(address(superchainWeth), _user, _wad); + + vm.expectEmit(address(superchainWeth)); + emit Transfer(_user, _user, _wad); + + // Act + vm.prank(Preinstalls.Permit2); + superchainWeth.transferFrom(_user, _user, _wad); + + // Assert + assertEq(superchainWeth.balanceOf(_user), _wad); + } } From d73a4bccb3cf252f61264643b6a0ba3bd190460a Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 24 Oct 2024 08:45:27 -0600 Subject: [PATCH 019/451] op-service, op-deployer: Support marshaling systemconfig in pre-Holocene format (#12619) op-deployer generates rollup configs, and those rollup configs are currently broken for older versions of op-node that don't support reading the EIP1559Params field in the SystemConfig. This PR adds a meta field within the SystemConfig that, when enabled, marshals the SystemConfig without the EIP1559Params field. This solution is backwards-compatible and minimally invasive, requiring no changes to the consensus-critical code that consumes the SystemConfig elsewhere. Closes https://github.com/ethereum-optimism/optimism/issues/12615. --- op-deployer/pkg/deployer/inspect/rollup.go | 3 ++ op-service/eth/types.go | 37 +++++++++++++++++++++- op-service/eth/types_test.go | 21 ++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/op-deployer/pkg/deployer/inspect/rollup.go b/op-deployer/pkg/deployer/inspect/rollup.go index 59e49c904541..d094427d951f 100644 --- a/op-deployer/pkg/deployer/inspect/rollup.go +++ b/op-deployer/pkg/deployer/inspect/rollup.go @@ -22,6 +22,9 @@ func RollupCLI(cliCtx *cli.Context) error { } _, rollupConfig, err := GenesisAndRollup(globalState, cfg.ChainID) + if rollupConfig.HoloceneTime == nil { + rollupConfig.Genesis.SystemConfig.MarshalPreHolocene = true + } if err != nil { return fmt.Errorf("failed to generate rollup config: %w", err) } diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 8cbc9eb7eec7..e23a903ebca4 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -3,6 +3,7 @@ package eth import ( "bytes" "encoding/binary" + "encoding/json" "errors" "fmt" "math" @@ -419,8 +420,42 @@ type SystemConfig struct { // EIP1559Params contains the Holocene-encoded EIP-1559 parameters. This // value will be 0 if Holocene is not active, or if derivation has yet to // process any EIP_1559_PARAMS system config update events. - EIP1559Params Bytes8 `json:"eip1559Params,omitempty"` + EIP1559Params Bytes8 `json:"eip1559Params"` // More fields can be added for future SystemConfig versions. + + // MarshalPreHolocene indicates whether or not this struct should be + // marshaled in the pre-Holocene format. The pre-Holocene format does + // not marshal the EIP1559Params field. The presence of this field in + // pre-Holocene codebases causes the rollup config to be rejected. + MarshalPreHolocene bool `json:"-"` +} + +func (sysCfg SystemConfig) MarshalJSON() ([]byte, error) { + if sysCfg.MarshalPreHolocene { + return jsonMarshalPreHolocene(sysCfg) + } + return jsonMarshalHolocene(sysCfg) +} + +func jsonMarshalHolocene(sysCfg SystemConfig) ([]byte, error) { + type sysCfgMarshaling SystemConfig + return json.Marshal(sysCfgMarshaling(sysCfg)) +} + +func jsonMarshalPreHolocene(sysCfg SystemConfig) ([]byte, error) { + type sysCfgMarshaling struct { + BatcherAddr common.Address `json:"batcherAddr"` + Overhead Bytes32 `json:"overhead"` + Scalar Bytes32 `json:"scalar"` + GasLimit uint64 `json:"gasLimit"` + } + sc := sysCfgMarshaling{ + BatcherAddr: sysCfg.BatcherAddr, + Overhead: sysCfg.Overhead, + Scalar: sysCfg.Scalar, + GasLimit: sysCfg.GasLimit, + } + return json.Marshal(sc) } // The Ecotone upgrade introduces a versioned L1 scalar format diff --git a/op-service/eth/types_test.go b/op-service/eth/types_test.go index f62c14691a16..36aae514be48 100644 --- a/op-service/eth/types_test.go +++ b/op-service/eth/types_test.go @@ -1,10 +1,13 @@ package eth import ( + "encoding/json" "errors" "math" "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" ) @@ -66,3 +69,21 @@ func FuzzEncodeScalar(f *testing.F) { require.Equal(t, baseFeeScalar, scalars.BaseFeeScalar) }) } + +func TestSystemConfigMarshaling(t *testing.T) { + sysConfig := SystemConfig{ + BatcherAddr: common.Address{'A'}, + Overhead: Bytes32{0x4, 0x5, 0x6}, + Scalar: Bytes32{0x7, 0x8, 0x9}, + GasLimit: 1234, + // Leave EIP1559 params empty to prove that the + // zero value is sent. + } + j, err := json.Marshal(sysConfig) + require.NoError(t, err) + require.Equal(t, `{"batcherAddr":"0x4100000000000000000000000000000000000000","overhead":"0x0405060000000000000000000000000000000000000000000000000000000000","scalar":"0x0708090000000000000000000000000000000000000000000000000000000000","gasLimit":1234,"eip1559Params":"0x0000000000000000"}`, string(j)) + sysConfig.MarshalPreHolocene = true + j, err = json.Marshal(sysConfig) + require.NoError(t, err) + require.Equal(t, `{"batcherAddr":"0x4100000000000000000000000000000000000000","overhead":"0x0405060000000000000000000000000000000000000000000000000000000000","scalar":"0x0708090000000000000000000000000000000000000000000000000000000000","gasLimit":1234}`, string(j)) +} From 21527c63e80e05b35a5fdafd2cdae6ac42a72174 Mon Sep 17 00:00:00 2001 From: mbaxter Date: Thu, 24 Oct 2024 11:12:32 -0400 Subject: [PATCH 020/451] op-program: Compile op-program for Cannon64 (#12574) * op-program: Compile op-program for Cannon64 * op-program: Update comment --- op-program/Dockerfile.repro | 19 +++++++++++++------ op-program/Makefile | 13 ++++++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/op-program/Dockerfile.repro b/op-program/Dockerfile.repro index e3293b7908f7..fd713162e013 100644 --- a/op-program/Dockerfile.repro +++ b/op-program/Dockerfile.repro @@ -26,17 +26,16 @@ ARG OP_PROGRAM_VERSION=v0.0.0 ARG TARGETOS TARGETARCH -# Build the cannon, op-program, and op-program-client.elf binaries. +# Build the cannon and op-program-client.elf binaries. RUN --mount=type=cache,target=/root/.cache/go-build cd cannon && make cannon \ GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$CANNON_VERSION" -RUN --mount=type=cache,target=/root/.cache/go-build cd op-program && make op-program-host \ - GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_PROGRAM_VERSION" RUN --mount=type=cache,target=/root/.cache/go-build cd op-program && make op-program-client-mips \ GOOS=linux GOARCH=mips GOMIPS=softfloat GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_PROGRAM_VERSION" # Run the op-program-client.elf binary directly through cannon's load-elf subcommand. -RUN /app/cannon/bin/cannon load-elf --type singlethreaded-2 --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate.bin.gz --meta "" -RUN /app/cannon/bin/cannon load-elf --type multithreaded --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate-mt.bin.gz --meta "" +RUN /app/cannon/bin/cannon load-elf --type singlethreaded-2 --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate.bin.gz --meta "/app/op-program/bin/meta.json" +RUN /app/cannon/bin/cannon load-elf --type multithreaded --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate-mt.bin.gz --meta "/app/op-program/bin/meta-mt.json" +RUN /app/cannon/bin/cannon load-elf --type multithreaded64 --path /app/op-program/bin/op-program-client64.elf --out /app/op-program/bin/prestate-mt64.bin.gz --meta "/app/op-program/bin/meta-mt64.json" # Generate the prestate proof containing the absolute pre-state hash. RUN /app/cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input /app/op-program/bin/prestate.bin.gz --meta "" --proof-fmt '/app/op-program/bin/%d.json' --output "" @@ -45,13 +44,21 @@ RUN mv /app/op-program/bin/0.json /app/op-program/bin/prestate-proof.json RUN /app/cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input /app/op-program/bin/prestate-mt.bin.gz --meta "" --proof-fmt '/app/op-program/bin/%d-mt.json' --output "" RUN mv /app/op-program/bin/0-mt.json /app/op-program/bin/prestate-proof-mt.json +RUN /app/cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input /app/op-program/bin/prestate-mt64.bin.gz --meta "" --proof-fmt '/app/op-program/bin/%d-mt64.json' --output "" +RUN mv /app/op-program/bin/0-mt64.json /app/op-program/bin/prestate-proof-mt64.json + # Exports files to the specified output location. # Writing files to host requires buildkit to be enabled. # e.g. `BUILDKIT=1 docker build ...` FROM scratch AS export-stage -COPY --from=builder /app/op-program/bin/op-program . COPY --from=builder /app/op-program/bin/op-program-client.elf . +COPY --from=builder /app/op-program/bin/op-program-client64.elf . +COPY --from=builder /app/op-program/bin/meta.json . COPY --from=builder /app/op-program/bin/prestate.bin.gz . COPY --from=builder /app/op-program/bin/prestate-proof.json . +COPY --from=builder /app/op-program/bin/meta-mt.json . COPY --from=builder /app/op-program/bin/prestate-mt.bin.gz . COPY --from=builder /app/op-program/bin/prestate-proof-mt.json . +COPY --from=builder /app/op-program/bin/meta-mt64.json . +COPY --from=builder /app/op-program/bin/prestate-mt64.bin.gz . +COPY --from=builder /app/op-program/bin/prestate-proof-mt64.json . diff --git a/op-program/Makefile b/op-program/Makefile index 983c795ac929..30cbaeea4c42 100644 --- a/op-program/Makefile +++ b/op-program/Makefile @@ -26,11 +26,18 @@ op-program-host: op-program-client: env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client ./client/cmd/main.go -op-program-client-mips: +op-program-client-mips: op-program-client-mips32 op-program-client-mips64 + +op-program-client-mips32: env GO111MODULE=on GOOS=linux GOARCH=mips GOMIPS=softfloat go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client.elf ./client/cmd/main.go # verify output with: readelf -h bin/op-program-client.elf # result is mips32, big endian, R3000 +op-program-client-mips64: + env GO111MODULE=on GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client64.elf ./client/cmd/main.go + # verify output with: readelf -h bin/op-program-client64.elf + # result is mips64, big endian, R3000 + op-program-client-riscv: env GO111MODULE=on GOOS=linux GOARCH=riscv64 go build -v -gcflags="all=-d=softfloat" -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client-riscv.elf ./client/cmd/main.go @@ -40,6 +47,8 @@ reproducible-prestate: @cat ./bin/prestate-proof.json | jq -r .pre @echo "MT-Cannon Absolute prestate hash: " @cat ./bin/prestate-proof-mt.json | jq -r .pre + @echo "Cannon64 Absolute prestate hash: " + @cat ./bin/prestate-proof-mt64.json | jq -r .pre .PHONY: reproducible-prestate verify-reproducibility: @@ -101,6 +110,8 @@ verify-compat: verify-sepolia-delta verify-sepolia-ecotone verify-mainnet-genesi op-program-host \ op-program-client \ op-program-client-mips \ + op-program-client-mips32 \ + op-program-client-mips64 \ op-program-client-riscv \ clean \ test \ From a71c4926cf8ee3559fa7b159e3ae8ce22ef3f2c1 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 24 Oct 2024 18:20:28 +0200 Subject: [PATCH 021/451] op-supervisor: DB improvements for cross-safe updates (#12622) Co-authored-by: axelKingsley Co-authored-by: Tyler Smith --- op-supervisor/supervisor/backend/backend.go | 11 +- .../supervisor/backend/backend_test.go | 5 +- op-supervisor/supervisor/backend/db/db.go | 27 +- .../supervisor/backend/db/fromda/db.go | 162 ++++++++++- .../supervisor/backend/db/fromda/db_test.go | 265 ++++++++++++++++-- .../supervisor/backend/db/fromda/entry.go | 5 +- .../supervisor/backend/db/fromda/update.go | 17 +- .../backend/db/fromda/update_test.go | 31 +- .../supervisor/backend/db/logs/db.go | 117 ++++++-- .../supervisor/backend/db/logs/db_test.go | 156 ++++++++--- .../supervisor/backend/db/logs/entries.go | 15 +- .../supervisor/backend/db/logs/iterator.go | 11 +- .../supervisor/backend/db/logs/state.go | 18 +- op-supervisor/supervisor/backend/db/query.go | 230 ++++++++++++++- op-supervisor/supervisor/backend/db/update.go | 17 +- .../backend/processors/contracts/l2inbox.go | 2 +- .../processors/contracts/l2inbox_test.go | 4 +- .../{backend/db/entrydb => types}/error.go | 9 +- op-supervisor/supervisor/types/types.go | 47 +++- 19 files changed, 972 insertions(+), 177 deletions(-) rename op-supervisor/supervisor/{backend/db/entrydb => types}/error.go (63%) diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 7a1eb2eaf78a..27ed1ec3e984 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -17,7 +17,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-supervisor/config" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend" @@ -71,7 +70,7 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg chains := depSet.Chains() // create initial per-chain resources - chainsDBs := db.NewChainsDB(logger) + chainsDBs := db.NewChainsDB(logger, depSet) chainProcessors := make(map[types.ChainID]*processors.ChainProcessor, len(chains)) chainMetrics := make(map[types.ChainID]*chainMetrics, len(chains)) @@ -167,7 +166,7 @@ func (su *SupervisorBackend) attachRPC(ctx context.Context, rpc string) error { return err } if !su.depSet.HasChain(chainID) { - return fmt.Errorf("chain %s is not part of the interop dependency set: %w", chainID, db.ErrUnknownChain) + return fmt.Errorf("chain %s is not part of the interop dependency set: %w", chainID, types.ErrUnknownChain) } cm, ok := su.chainMetrics[chainID] if !ok { @@ -273,10 +272,10 @@ func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHa blockNum := identifier.BlockNumber logIdx := identifier.LogIndex _, err := su.chainDBs.Check(chainID, blockNum, uint32(logIdx), payloadHash) - if errors.Is(err, entrydb.ErrFuture) { + if errors.Is(err, types.ErrFuture) { return types.LocalUnsafe, nil } - if errors.Is(err, entrydb.ErrConflict) { + if errors.Is(err, types.ErrConflict) { return types.Invalid, nil } if err != nil { @@ -378,7 +377,7 @@ func (su *SupervisorBackend) UpdateLocalUnsafe(chainID types.ChainID, head eth.B defer su.mu.RUnlock() ch, ok := su.chainProcessors[chainID] if !ok { - return db.ErrUnknownChain + return types.ErrUnknownChain } return ch.OnNewHead(head) } diff --git a/op-supervisor/supervisor/backend/backend_test.go b/op-supervisor/supervisor/backend/backend_test.go index 5bdb49caaaf9..80c773a5ea46 100644 --- a/op-supervisor/supervisor/backend/backend_test.go +++ b/op-supervisor/supervisor/backend/backend_test.go @@ -21,7 +21,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-supervisor/config" "github.com/ethereum-optimism/optimism/op-supervisor/metrics" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -89,7 +88,7 @@ func TestBackendLifetime(t *testing.T) { t.Log("started!") _, err = b.UnsafeView(context.Background(), chainA, types.ReferenceView{}) - require.ErrorIs(t, err, entrydb.ErrFuture, "no data yet, need local-unsafe") + require.ErrorIs(t, err, types.ErrFuture, "no data yet, need local-unsafe") src.ExpectL1BlockRefByNumber(0, blockX, nil) src.ExpectFetchReceipts(blockX.Hash, &testutils.MockBlockInfo{ @@ -118,7 +117,7 @@ func TestBackendLifetime(t *testing.T) { b.chainProcessors[chainA].ProcessToHead() _, err = b.UnsafeView(context.Background(), chainA, types.ReferenceView{}) - require.ErrorIs(t, err, entrydb.ErrFuture, "still no data yet, need cross-unsafe") + require.ErrorIs(t, err, types.ErrFuture, "still no data yet, need cross-unsafe") err = b.chainDBs.UpdateCrossUnsafe(chainA, types.BlockSeal{ Hash: blockX.Hash, diff --git a/op-supervisor/supervisor/backend/db/db.go b/op-supervisor/supervisor/backend/db/db.go index ab8a9a652e5b..922f849dea05 100644 --- a/op-supervisor/supervisor/backend/db/db.go +++ b/op-supervisor/supervisor/backend/db/db.go @@ -12,13 +12,10 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/fromda" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/logs" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) -var ( - ErrUnknownChain = errors.New("unknown chain") -) - type LogStorage interface { io.Closer @@ -46,13 +43,22 @@ type LogStorage interface { // The block-seal of the blockNum block, that the log was included in, is returned. // This seal may be fully zeroed, without error, if the block isn't fully known yet. Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) + + // OpenBlock accumulates the ExecutingMessage events for a block and returns them + OpenBlock(blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) } type LocalDerivedFromStorage interface { + First() (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) Latest() (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) AddDerived(derivedFrom eth.BlockRef, derived eth.BlockRef) error LastDerivedAt(derivedFrom eth.BlockID) (derived types.BlockSeal, err error) DerivedFrom(derived eth.BlockID) (derivedFrom types.BlockSeal, err error) + FirstAfter(derivedFrom, derived eth.BlockID) (nextDerivedFrom, nextDerived types.BlockSeal, err error) + NextDerivedFrom(derivedFrom eth.BlockID) (nextDerivedFrom types.BlockSeal, err error) + NextDerived(derived eth.BlockID) (derivedFrom types.BlockSeal, nextDerived types.BlockSeal, err error) + PreviousDerivedFrom(derivedFrom eth.BlockID) (prevDerivedFrom types.BlockSeal, err error) + PreviousDerived(derived eth.BlockID) (prevDerived types.BlockSeal, err error) } var _ LocalDerivedFromStorage = (*fromda.DB)(nil) @@ -65,7 +71,7 @@ type CrossDerivedFromStorage interface { var _ LogStorage = (*logs.DB)(nil) // ChainsDB is a database that stores logs and derived-from data for multiple chains. -// it implements the ChainsStorage interface. +// it implements the LogStorage interface, as well as several DB interfaces needed by the cross package. type ChainsDB struct { // RW mutex: // Read = chains can be read / mutated. @@ -90,16 +96,21 @@ type ChainsDB struct { // an error until it has this L1 finality to work with. finalizedL1 eth.L1BlockRef + // depSet is the dependency set, used to determine what may be tracked, + // what is missing, and to provide it to DB users. + depSet depset.DependencySet + logger log.Logger } -func NewChainsDB(l log.Logger) *ChainsDB { +func NewChainsDB(l log.Logger, depSet depset.DependencySet) *ChainsDB { return &ChainsDB{ logDBs: make(map[types.ChainID]LogStorage), logger: l, localDBs: make(map[types.ChainID]LocalDerivedFromStorage), crossDBs: make(map[types.ChainID]CrossDerivedFromStorage), crossUnsafe: make(map[types.ChainID]types.BlockSeal), + depSet: depSet, } } @@ -168,6 +179,10 @@ func (db *ChainsDB) ResumeFromLastSealedBlock() error { return nil } +func (db *ChainsDB) DependencySet() depset.DependencySet { + return db.depSet +} + func (db *ChainsDB) Close() error { db.mu.Lock() defer db.mu.Unlock() diff --git a/op-supervisor/supervisor/backend/db/fromda/db.go b/op-supervisor/supervisor/backend/db/fromda/db.go index 7016fc6b25cb..5ee609ecdfe7 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db.go +++ b/op-supervisor/supervisor/backend/db/fromda/db.go @@ -3,6 +3,7 @@ package fromda import ( "cmp" "fmt" + "io" "sort" "sync" @@ -64,12 +65,48 @@ func (db *DB) Rewind(derivedFrom uint64) error { return nil } +// First returns the first known values, alike to Latest. +func (db *DB) First() (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + lastIndex := db.store.LastEntryIdx() + if lastIndex < 0 { + return types.BlockSeal{}, types.BlockSeal{}, types.ErrFuture + } + last, err := db.readAt(0) + if err != nil { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("failed to read first derivation data: %w", err) + } + return last.derivedFrom, last.derived, nil +} + +func (db *DB) PreviousDerived(derived eth.BlockID) (prevDerived types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + // get the last time this L2 block was seen. + selfIndex, self, err := db.firstDerivedFrom(derived.Number) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("failed to find derived %d: %w", derived.Number, err) + } + if self.derived.ID() != derived { + return types.BlockSeal{}, fmt.Errorf("found %s, but expected %s: %w", self.derived, derived, types.ErrConflict) + } + if selfIndex == 0 { // genesis block has a zeroed block as parent block + return types.BlockSeal{}, nil + } + prev, err := db.readAt(selfIndex - 1) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("cannot find previous derived before %s: %w", derived, err) + } + return prev.derived, nil +} + // Latest returns the last known values: // derivedFrom: the L1 block that the L2 block is safe for (not necessarily the first, multiple L2 blocks may be derived from the same L1 block). // derived: the L2 block that was derived (not necessarily the first, the L1 block may have been empty and repeated the last safe L2 block). func (db *DB) Latest() (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { - db.rwLock.Lock() - defer db.rwLock.Unlock() + db.rwLock.RLock() + defer db.rwLock.RUnlock() return db.latest() } @@ -77,7 +114,7 @@ func (db *DB) Latest() (derivedFrom types.BlockSeal, derived types.BlockSeal, er func (db *DB) latest() (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { lastIndex := db.store.LastEntryIdx() if lastIndex < 0 { - return types.BlockSeal{}, types.BlockSeal{}, entrydb.ErrFuture + return types.BlockSeal{}, types.BlockSeal{}, types.ErrFuture } last, err := db.readAt(lastIndex) if err != nil { @@ -96,11 +133,30 @@ func (db *DB) LastDerivedAt(derivedFrom eth.BlockID) (derived types.BlockSeal, e } if link.derivedFrom.ID() != derivedFrom { return types.BlockSeal{}, fmt.Errorf("searched for last derived-from %s but found %s: %w", - derivedFrom, link.derivedFrom, entrydb.ErrConflict) + derivedFrom, link.derivedFrom, types.ErrConflict) } return link.derived, nil } +// NextDerived finds the next L2 block after derived, and what it was derived from +func (db *DB) NextDerived(derived eth.BlockID) (derivedFrom types.BlockSeal, nextDerived types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + // get the last time this L2 block was seen. + selfIndex, self, err := db.lastDerivedFrom(derived.Number) + if err != nil { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("failed to find derived %d: %w", derived.Number, err) + } + if self.derived.ID() != derived { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("found %s, but expected %s: %w", self.derived, derived, types.ErrConflict) + } + next, err := db.readAt(selfIndex + 1) + if err != nil { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("cannot find next derived after %s: %w", derived, err) + } + return next.derivedFrom, next.derived, nil +} + // DerivedFrom determines where a L2 block was first derived from. // (a L2 block may repeat if the following L1 blocks are empty and don't produce additional L2 blocks) func (db *DB) DerivedFrom(derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { @@ -112,17 +168,94 @@ func (db *DB) DerivedFrom(derived eth.BlockID) (derivedFrom types.BlockSeal, err } if link.derived.ID() != derived { return types.BlockSeal{}, fmt.Errorf("searched for first derived %s but found %s: %w", - derived, link.derived, entrydb.ErrConflict) + derived, link.derived, types.ErrConflict) } return link.derivedFrom, nil } +func (db *DB) PreviousDerivedFrom(derivedFrom eth.BlockID) (prevDerivedFrom types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + // get the last time this L1 block was seen. + selfIndex, self, err := db.firstDerivedAt(derivedFrom.Number) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("failed to find derived %d: %w", derivedFrom.Number, err) + } + if self.derivedFrom.ID() != derivedFrom { + return types.BlockSeal{}, fmt.Errorf("found %s, but expected %s: %w", self.derivedFrom, derivedFrom, types.ErrConflict) + } + if selfIndex == 0 { // genesis block has a zeroed block as parent block + return types.BlockSeal{}, nil + } + prev, err := db.readAt(selfIndex - 1) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("cannot find previous derived before %s: %w", derivedFrom, err) + } + return prev.derivedFrom, nil +} + +// NextDerivedFrom finds the next L1 block after derivedFrom +func (db *DB) NextDerivedFrom(derivedFrom eth.BlockID) (nextDerivedFrom types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + selfIndex, self, err := db.lastDerivedAt(derivedFrom.Number) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("failed to find derived-from %d: %w", derivedFrom.Number, err) + } + if self.derivedFrom.ID() != derivedFrom { + return types.BlockSeal{}, fmt.Errorf("found %s, but expected %s: %w", self.derivedFrom, derivedFrom, types.ErrConflict) + } + next, err := db.readAt(selfIndex + 1) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("cannot find next derived-from after %s: %w", derivedFrom, err) + } + return next.derivedFrom, nil +} + +// FirstAfter determines the next entry after the given pair of derivedFrom, derived. +// Either one or both of the two entries will be an increment by 1 +func (db *DB) FirstAfter(derivedFrom, derived eth.BlockID) (nextDerivedFrom, nextDerived types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + selfIndex, selfLink, err := db.lookup(derivedFrom.Number, derived.Number) + if err != nil { + return types.BlockSeal{}, types.BlockSeal{}, err + } + if selfLink.derivedFrom.ID() != derivedFrom { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("DB has derived-from %s but expected %s: %w", selfLink.derivedFrom, derivedFrom, types.ErrConflict) + } + if selfLink.derived.ID() != derived { + return types.BlockSeal{}, types.BlockSeal{}, fmt.Errorf("DB has derived %s but expected %s: %w", selfLink.derived, derived, types.ErrConflict) + } + next, err := db.readAt(selfIndex + 1) + if err != nil { + return types.BlockSeal{}, types.BlockSeal{}, err + } + return next.derivedFrom, next.derived, nil +} + +func (db *DB) lastDerivedFrom(derived uint64) (entrydb.EntryIdx, LinkEntry, error) { + return db.find(true, func(link LinkEntry) int { + return cmp.Compare(derived, link.derived.Number) + }) +} + func (db *DB) firstDerivedFrom(derived uint64) (entrydb.EntryIdx, LinkEntry, error) { return db.find(false, func(link LinkEntry) int { return cmp.Compare(link.derived.Number, derived) }) } +func (db *DB) lookup(derivedFrom, derived uint64) (entrydb.EntryIdx, LinkEntry, error) { + return db.find(false, func(link LinkEntry) int { + res := cmp.Compare(link.derived.Number, derived) + if res == 0 { + return cmp.Compare(link.derivedFrom.Number, derivedFrom) + } + return res + }) +} + func (db *DB) lastDerivedAt(derivedFrom uint64) (entrydb.EntryIdx, LinkEntry, error) { // Reverse: prioritize the last entry. return db.find(true, func(link LinkEntry) int { @@ -130,13 +263,19 @@ func (db *DB) lastDerivedAt(derivedFrom uint64) (entrydb.EntryIdx, LinkEntry, er }) } +func (db *DB) firstDerivedAt(derivedFrom uint64) (entrydb.EntryIdx, LinkEntry, error) { + return db.find(false, func(link LinkEntry) int { + return cmp.Compare(link.derivedFrom.Number, derivedFrom) + }) +} + // find finds the first entry for which cmpFn(link) returns 0. // The cmpFn entries to the left should return -1, entries to the right 1. // If reverse, the cmpFn should be flipped too, and the last entry for which cmpFn(link) is 0 will be found. func (db *DB) find(reverse bool, cmpFn func(link LinkEntry) int) (entrydb.EntryIdx, LinkEntry, error) { n := db.store.Size() if n == 0 { - return -1, LinkEntry{}, entrydb.ErrFuture + return -1, LinkEntry{}, types.ErrFuture } var searchErr error // binary-search for the smallest index i for which cmp(i) >= 0 @@ -157,9 +296,9 @@ func (db *DB) find(reverse bool, cmpFn func(link LinkEntry) int) (entrydb.EntryI } if result == int(n) { if reverse { - return -1, LinkEntry{}, fmt.Errorf("no entry found: %w", entrydb.ErrSkipped) + return -1, LinkEntry{}, fmt.Errorf("no entry found: %w", types.ErrSkipped) } else { - return -1, LinkEntry{}, fmt.Errorf("no entry found: %w", entrydb.ErrFuture) + return -1, LinkEntry{}, fmt.Errorf("no entry found: %w", types.ErrFuture) } } if reverse { @@ -171,9 +310,9 @@ func (db *DB) find(reverse bool, cmpFn func(link LinkEntry) int) (entrydb.EntryI } if cmpFn(link) != 0 { if reverse { - return -1, LinkEntry{}, fmt.Errorf("lowest entry %s is too high: %w", link, entrydb.ErrFuture) + return -1, LinkEntry{}, fmt.Errorf("lowest entry %s is too high: %w", link, types.ErrFuture) } else { - return -1, LinkEntry{}, fmt.Errorf("lowest entry %s is too high: %w", link, entrydb.ErrSkipped) + return -1, LinkEntry{}, fmt.Errorf("lowest entry %s is too high: %w", link, types.ErrSkipped) } } if cmpFn(link) != 0 { @@ -187,6 +326,9 @@ func (db *DB) find(reverse bool, cmpFn func(link LinkEntry) int) (entrydb.EntryI func (db *DB) readAt(i entrydb.EntryIdx) (LinkEntry, error) { entry, err := db.store.Read(i) if err != nil { + if err == io.EOF { + return LinkEntry{}, types.ErrFuture + } return LinkEntry{}, err } var out LinkEntry diff --git a/op-supervisor/supervisor/backend/db/fromda/db_test.go b/op-supervisor/supervisor/backend/db/fromda/db_test.go index bfc44ec00aaa..fbcf82ab194e 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db_test.go +++ b/op-supervisor/supervisor/backend/db/fromda/db_test.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -74,16 +73,31 @@ func TestEmptyDB(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) {}, func(t *testing.T, db *DB, m *stubMetrics) { _, _, err := db.Latest() - require.ErrorIs(t, err, entrydb.ErrFuture) + require.ErrorIs(t, err, types.ErrFuture) - _, _, err = db.Latest() - require.ErrorIs(t, err, entrydb.ErrFuture) + _, _, err = db.First() + require.ErrorIs(t, err, types.ErrFuture) _, err = db.LastDerivedAt(eth.BlockID{}) - require.ErrorIs(t, err, entrydb.ErrFuture) + require.ErrorIs(t, err, types.ErrFuture) _, err = db.DerivedFrom(eth.BlockID{}) - require.ErrorIs(t, err, entrydb.ErrFuture) + require.ErrorIs(t, err, types.ErrFuture) + + _, err = db.PreviousDerived(eth.BlockID{}) + require.ErrorIs(t, err, types.ErrFuture) + + _, _, err = db.NextDerived(eth.BlockID{}) + require.ErrorIs(t, err, types.ErrFuture) + + _, err = db.PreviousDerivedFrom(eth.BlockID{}) + require.ErrorIs(t, err, types.ErrFuture) + + _, err = db.NextDerivedFrom(eth.BlockID{}) + require.ErrorIs(t, err, types.ErrFuture) + + _, _, err = db.FirstAfter(eth.BlockID{}, eth.BlockID{}) + require.ErrorIs(t, err, types.ErrFuture) }) } @@ -129,23 +143,63 @@ func TestSingleEntryDB(t *testing.T) { require.Equal(t, expectedDerivedFrom, derivedFrom) require.Equal(t, expectedDerived, derived) + derivedFrom, derived, err = db.First() + require.NoError(t, err) + require.Equal(t, expectedDerivedFrom, derivedFrom) + require.Equal(t, expectedDerived, derived) + derived, err = db.LastDerivedAt(expectedDerivedFrom.ID()) require.NoError(t, err) require.Equal(t, expectedDerived, derived) _, err = db.LastDerivedAt(eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerivedFrom.Number}) - require.ErrorIs(t, err, entrydb.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) + + // No block known, yet, after the given block pair + _, _, err = db.FirstAfter(derivedFrom.ID(), derived.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + // Not after a non-existent block pair + _, _, err = db.FirstAfter(eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerivedFrom.Number}, expectedDerived.ID()) + require.ErrorIs(t, err, types.ErrConflict) + _, _, err = db.FirstAfter(expectedDerivedFrom.ID(), eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerived.Number}) + require.ErrorIs(t, err, types.ErrConflict) derivedFrom, err = db.DerivedFrom(expectedDerived.ID()) require.NoError(t, err) require.Equal(t, expectedDerivedFrom, derivedFrom) _, err = db.DerivedFrom(eth.BlockID{Hash: common.Hash{0xbb}, Number: expectedDerived.Number}) - require.ErrorIs(t, err, entrydb.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) + + prev, err := db.PreviousDerived(expectedDerived.ID()) + require.NoError(t, err) + require.Equal(t, types.BlockSeal{}, prev, "zeroed seal before first entry") + + _, _, err = db.NextDerived(expectedDerived.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + // if 1 was the first inserted entry, then we skipped 0 + _, _, err = db.NextDerived(mockL2(0).ID()) + require.ErrorIs(t, err, types.ErrSkipped) + + prev, err = db.PreviousDerivedFrom(expectedDerivedFrom.ID()) + require.NoError(t, err) + require.Equal(t, types.BlockSeal{}, prev, "zeroed seal before first entry") + + _, err = db.NextDerivedFrom(expectedDerivedFrom.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + // if 1 was the first inserted entry, then we skipped 0 + _, err = db.NextDerivedFrom(mockL1(0).ID()) + require.ErrorIs(t, err, types.ErrSkipped) + + _, _, err = db.FirstAfter(expectedDerivedFrom.ID(), expectedDerived.ID()) + require.ErrorIs(t, err, types.ErrFuture) }) } -func TestTwoEntryDB(t *testing.T) { +func TestThreeEntryDB(t *testing.T) { l1Block0 := mockL1(0) l1Block1 := mockL1(1) l1Block2 := mockL1(2) @@ -165,19 +219,24 @@ func TestTwoEntryDB(t *testing.T) { require.Equal(t, l1Block2, derivedFrom) require.Equal(t, l2Block2, derived) + derivedFrom, derived, err = db.First() + require.NoError(t, err) + require.Equal(t, l1Block0, derivedFrom) + require.Equal(t, l2Block0, derived) + derived, err = db.LastDerivedAt(l1Block2.ID()) require.NoError(t, err) require.Equal(t, l2Block2, derived) _, err = db.LastDerivedAt(eth.BlockID{Hash: common.Hash{0xaa}, Number: l1Block2.Number}) - require.ErrorIs(t, err, entrydb.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) derivedFrom, err = db.DerivedFrom(l2Block2.ID()) require.NoError(t, err) require.Equal(t, l1Block2, derivedFrom) _, err = db.DerivedFrom(eth.BlockID{Hash: common.Hash{0xbb}, Number: l2Block2.Number}) - require.ErrorIs(t, err, entrydb.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) derived, err = db.LastDerivedAt(l1Block1.ID()) require.NoError(t, err) @@ -194,6 +253,67 @@ func TestTwoEntryDB(t *testing.T) { derivedFrom, err = db.DerivedFrom(l2Block0.ID()) require.NoError(t, err) require.Equal(t, l1Block0, derivedFrom) + + derived, err = db.PreviousDerived(l2Block0.ID()) + require.NoError(t, err) + require.Equal(t, types.BlockSeal{}, derived) + + derived, err = db.PreviousDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l2Block0, derived) + + derived, err = db.PreviousDerived(l2Block2.ID()) + require.NoError(t, err) + require.Equal(t, l2Block1, derived) + + derivedFrom, derived, err = db.NextDerived(l2Block0.ID()) + require.NoError(t, err) + require.Equal(t, l2Block1, derived) + require.Equal(t, l1Block1, derivedFrom) + + derivedFrom, derived, err = db.NextDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l2Block2, derived) + require.Equal(t, l1Block2, derivedFrom) + + _, _, err = db.NextDerived(l2Block2.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, err = db.PreviousDerivedFrom(l1Block0.ID()) + require.NoError(t, err) + require.Equal(t, types.BlockSeal{}, derivedFrom) + + derivedFrom, err = db.PreviousDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block0, derivedFrom) + + derivedFrom, err = db.PreviousDerivedFrom(l1Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + + derivedFrom, err = db.NextDerivedFrom(l1Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + + derivedFrom, err = db.NextDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) + + _, err = db.NextDerivedFrom(l1Block2.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + _, _, err = db.FirstAfter(l1Block2.ID(), l2Block2.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, derived, err = db.FirstAfter(l1Block0.ID(), l2Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block1, derived) + + derivedFrom, derived, err = db.FirstAfter(l1Block1.ID(), l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) + require.Equal(t, l2Block2, derived) }) } @@ -249,6 +369,65 @@ func TestFastL2Batcher(t *testing.T) { require.NoError(t, err) require.Equal(t, l2Block4, derived) + derived, err = db.PreviousDerived(l2Block5.ID()) + require.NoError(t, err) + require.Equal(t, l2Block4, derived) + derived, err = db.PreviousDerived(l2Block4.ID()) + require.NoError(t, err) + require.Equal(t, l2Block3, derived) + derived, err = db.PreviousDerived(l2Block3.ID()) + require.NoError(t, err) + require.Equal(t, l2Block2, derived) + derived, err = db.PreviousDerived(l2Block2.ID()) + require.NoError(t, err) + require.Equal(t, l2Block1, derived) + derived, err = db.PreviousDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l2Block0, derived) + + derivedFrom, derived, err = db.NextDerived(l2Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block1, derived) + derivedFrom, derived, err = db.NextDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block2, derived) + derivedFrom, derived, err = db.NextDerived(l2Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block3, derived) + derivedFrom, derived, err = db.NextDerived(l2Block3.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block4, derived) + derivedFrom, derived, err = db.NextDerived(l2Block4.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) // derived from later L1 block + require.Equal(t, l2Block5, derived) + _, _, err = db.NextDerived(l2Block5.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, err = db.PreviousDerivedFrom(l1Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + derivedFrom, err = db.PreviousDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block0, derivedFrom) + + derivedFrom, err = db.NextDerivedFrom(l1Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + derivedFrom, err = db.NextDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) + _, err = db.NextDerivedFrom(l1Block2.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, derived, err = db.FirstAfter(l1Block1.ID(), l2Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) // no increment in L1 yet, the next after is L2 block 3 + require.Equal(t, l2Block3, derived) }) } @@ -299,6 +478,60 @@ func TestSlowL2Batcher(t *testing.T) { derivedFrom, err = db.DerivedFrom(l2Block1.ID()) require.NoError(t, err) require.Equal(t, l1Block1, derivedFrom) + + derived, err = db.PreviousDerived(l2Block2.ID()) + require.NoError(t, err) + require.Equal(t, l2Block1, derived) + derived, err = db.PreviousDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l2Block0, derived) + + derivedFrom, derived, err = db.NextDerived(l2Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + require.Equal(t, l2Block1, derived) + derivedFrom, derived, err = db.NextDerived(l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block5, derivedFrom) + require.Equal(t, l2Block2, derived) + _, _, err = db.NextDerived(l2Block2.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, err = db.PreviousDerivedFrom(l1Block5.ID()) + require.NoError(t, err) + require.Equal(t, l1Block4, derivedFrom) + derivedFrom, err = db.PreviousDerivedFrom(l1Block4.ID()) + require.NoError(t, err) + require.Equal(t, l1Block3, derivedFrom) + derivedFrom, err = db.PreviousDerivedFrom(l1Block3.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) + derivedFrom, err = db.PreviousDerivedFrom(l1Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + derivedFrom, err = db.PreviousDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block0, derivedFrom) + + derivedFrom, err = db.NextDerivedFrom(l1Block0.ID()) + require.NoError(t, err) + require.Equal(t, l1Block1, derivedFrom) + derivedFrom, err = db.NextDerivedFrom(l1Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block2, derivedFrom) + derivedFrom, err = db.NextDerivedFrom(l1Block2.ID()) + require.NoError(t, err) + require.Equal(t, l1Block3, derivedFrom) + derivedFrom, err = db.NextDerivedFrom(l1Block4.ID()) + require.NoError(t, err) + require.Equal(t, l1Block5, derivedFrom) + _, err = db.NextDerivedFrom(l1Block5.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + derivedFrom, derived, err = db.FirstAfter(l1Block2.ID(), l2Block1.ID()) + require.NoError(t, err) + require.Equal(t, l1Block3, derivedFrom) + require.Equal(t, l2Block1, derived) // no increment in L2 yet, the next after is L1 block 3 }) } @@ -378,17 +611,17 @@ func testManyEntryDB(t *testing.T, offsetL1 uint64, offsetL2 uint64) { // if not started at genesis, try to read older data, assert it's unavailable. if offsetL1 > 0 { _, err := db.LastDerivedAt(mockL1(0).ID()) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) _, err = db.LastDerivedAt(mockL1(offsetL1 - 1).ID()) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) } if offsetL2 > 0 { _, err := db.DerivedFrom(mockL2(0).ID()) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) _, err = db.DerivedFrom(mockL2(offsetL2 - 1).ID()) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) } }) } @@ -425,7 +658,7 @@ func TestRewind(t *testing.T) { require.Equal(t, l2Block2, derived) // Rewind to the future - require.ErrorIs(t, db.Rewind(6), entrydb.ErrFuture) + require.ErrorIs(t, db.Rewind(6), types.ErrFuture) // Rewind to the exact block we're at require.NoError(t, db.Rewind(l1Block5.Number)) diff --git a/op-supervisor/supervisor/backend/db/fromda/entry.go b/op-supervisor/supervisor/backend/db/fromda/entry.go index 651643b4eb43..4e4f9876d3e8 100644 --- a/op-supervisor/supervisor/backend/db/fromda/entry.go +++ b/op-supervisor/supervisor/backend/db/fromda/entry.go @@ -5,7 +5,6 @@ import ( "fmt" "io" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -57,10 +56,10 @@ func (d LinkEntry) String() string { func (d *LinkEntry) decode(e Entry) error { if e.Type() != DerivedFromV0 { - return fmt.Errorf("%w: unexpected entry type: %s", entrydb.ErrDataCorruption, e.Type()) + return fmt.Errorf("%w: unexpected entry type: %s", types.ErrDataCorruption, e.Type()) } if [3]byte(e[1:4]) != ([3]byte{}) { - return fmt.Errorf("%w: expected empty data, to pad entry size to round number: %x", entrydb.ErrDataCorruption, e[1:4]) + return fmt.Errorf("%w: expected empty data, to pad entry size to round number: %x", types.ErrDataCorruption, e[1:4]) } offset := 4 d.derivedFrom.Number = binary.BigEndian.Uint64(e[offset : offset+8]) diff --git a/op-supervisor/supervisor/backend/db/fromda/update.go b/op-supervisor/supervisor/backend/db/fromda/update.go index 07174cb8495a..146e558cf266 100644 --- a/op-supervisor/supervisor/backend/db/fromda/update.go +++ b/op-supervisor/supervisor/backend/db/fromda/update.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -60,19 +59,19 @@ func (db *DB) AddDerived(derivedFrom eth.BlockRef, derived eth.BlockRef) error { // I.e. we encountered an empty L1 block, and the same L2 block continues to be the last block that was derived from it. if lastDerived.Hash != derived.Hash { return fmt.Errorf("derived block %s conflicts with known derived block %s at same height: %w", - derived, lastDerived, entrydb.ErrConflict) + derived, lastDerived, types.ErrConflict) } } else if lastDerived.Number+1 == derived.Number { if lastDerived.Hash != derived.ParentHash { return fmt.Errorf("derived block %s (parent %s) does not build on %s: %w", - derived, derived.ParentHash, lastDerived, entrydb.ErrConflict) + derived, derived.ParentHash, lastDerived, types.ErrConflict) } } else if lastDerived.Number+1 < derived.Number { return fmt.Errorf("derived block %s (parent: %s) is too new, expected to build on top of %s: %w", - derived, derived.ParentHash, lastDerived, entrydb.ErrOutOfOrder) + derived, derived.ParentHash, lastDerived, types.ErrOutOfOrder) } else { return fmt.Errorf("derived block %s is older than current derived block %s: %w", - derived, lastDerived, entrydb.ErrOutOfOrder) + derived, lastDerived, types.ErrOutOfOrder) } // Check derived-from relation: multiple L2 blocks may be derived from the same L1 block. But everything in sequence. @@ -80,22 +79,22 @@ func (db *DB) AddDerived(derivedFrom eth.BlockRef, derived eth.BlockRef) error { // Same block height? Then it must be the same block. if lastDerivedFrom.Hash != derivedFrom.Hash { return fmt.Errorf("cannot add block %s as derived from %s, expected to be derived from %s at this block height: %w", - derived, derivedFrom, lastDerivedFrom, entrydb.ErrConflict) + derived, derivedFrom, lastDerivedFrom, types.ErrConflict) } } else if lastDerivedFrom.Number+1 == derivedFrom.Number { // parent hash check if lastDerivedFrom.Hash != derivedFrom.ParentHash { return fmt.Errorf("cannot add block %s as derived from %s (parent %s) derived on top of %s: %w", - derived, derivedFrom, derivedFrom.ParentHash, lastDerivedFrom, entrydb.ErrConflict) + derived, derivedFrom, derivedFrom.ParentHash, lastDerivedFrom, types.ErrConflict) } } else if lastDerivedFrom.Number+1 < derivedFrom.Number { // adding block that is derived from something too far into the future return fmt.Errorf("cannot add block %s as derived from %s, still deriving from %s: %w", - derived, derivedFrom, lastDerivedFrom, entrydb.ErrOutOfOrder) + derived, derivedFrom, lastDerivedFrom, types.ErrOutOfOrder) } else { // adding block that is derived from something too old return fmt.Errorf("cannot add block %s as derived from %s, deriving already at %s: %w", - derived, derivedFrom, lastDerivedFrom, entrydb.ErrOutOfOrder) + derived, derivedFrom, lastDerivedFrom, types.ErrOutOfOrder) } link := LinkEntry{ diff --git a/op-supervisor/supervisor/backend/db/fromda/update_test.go b/op-supervisor/supervisor/backend/db/fromda/update_test.go index 72141947110e..41be48ae0815 100644 --- a/op-supervisor/supervisor/backend/db/fromda/update_test.go +++ b/op-supervisor/supervisor/backend/db/fromda/update_test.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -42,14 +41,14 @@ func TestBadUpdates(t *testing.T) { { name: "add on old derivedFrom", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(bDerivedFrom, aDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(bDerivedFrom, aDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, { name: "repeat parent derivedFrom", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(cDerivedFrom, bDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(cDerivedFrom, bDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, @@ -60,14 +59,14 @@ func TestBadUpdates(t *testing.T) { Hash: common.Hash{0xba, 0xd}, Number: dDerivedFrom.Number, Timestamp: dDerivedFrom.Timestamp, - }, cDerivedFrom.Hash), toRef(eDerived, dDerived.Hash)), entrydb.ErrConflict) + }, cDerivedFrom.Hash), toRef(eDerived, dDerived.Hash)), types.ErrConflict) }, assertFn: noChange, }, { name: "DerivedFrom with conflicting parent root, same L1 height, new L2: accepted, L1 parent-hash is used only on L1 increments.", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.NoError(t, db.AddDerived(toRef(dDerivedFrom, common.Hash{0x42}), toRef(eDerived, dDerived.Hash)), entrydb.ErrConflict) + require.NoError(t, db.AddDerived(toRef(dDerivedFrom, common.Hash{0x42}), toRef(eDerived, dDerived.Hash)), types.ErrConflict) }, assertFn: func(t *testing.T, db *DB, m *stubMetrics) { derivedFrom, derived, err := db.Latest() @@ -79,28 +78,28 @@ func TestBadUpdates(t *testing.T) { { name: "Conflicting derivedFrom parent root, new L1 height, same L2", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(eDerivedFrom, common.Hash{0x42}), toRef(dDerived, cDerived.Hash)), entrydb.ErrConflict) + require.ErrorIs(t, db.AddDerived(toRef(eDerivedFrom, common.Hash{0x42}), toRef(dDerived, cDerived.Hash)), types.ErrConflict) }, assertFn: noChange, }, { name: "add on too new derivedFrom (even if parent-hash looks correct)", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(fDerivedFrom, dDerivedFrom.Hash), toRef(eDerived, dDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(fDerivedFrom, dDerivedFrom.Hash), toRef(eDerived, dDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, { name: "add on old derivedFrom (even if parent-hash looks correct)", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(cDerivedFrom, bDerivedFrom.Hash), toRef(cDerived, dDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(cDerivedFrom, bDerivedFrom.Hash), toRef(cDerived, dDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, { name: "add on even older derivedFrom", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(bDerivedFrom, aDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(bDerivedFrom, aDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, @@ -111,14 +110,14 @@ func TestBadUpdates(t *testing.T) { Hash: common.Hash{0x42}, Number: dDerived.Number, Timestamp: dDerived.Timestamp, - }, cDerived.Hash)), entrydb.ErrConflict) + }, cDerived.Hash)), types.ErrConflict) }, assertFn: noChange, }, { name: "add derived with conflicting parent hash, new L1 height, same L2 height: accepted, L2 parent-hash is only checked on L2 increments.", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.NoError(t, db.AddDerived(toRef(eDerivedFrom, dDerivedFrom.Hash), toRef(dDerived, common.Hash{0x42})), entrydb.ErrConflict) + require.NoError(t, db.AddDerived(toRef(eDerivedFrom, dDerivedFrom.Hash), toRef(dDerived, common.Hash{0x42})), types.ErrConflict) }, assertFn: func(t *testing.T, db *DB, m *stubMetrics) { derivedFrom, derived, err := db.Latest() @@ -130,28 +129,28 @@ func TestBadUpdates(t *testing.T) { { name: "add derived with conflicting parent hash, same L1 height, new L2 height", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(eDerived, common.Hash{0x42})), entrydb.ErrConflict) + require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(eDerived, common.Hash{0x42})), types.ErrConflict) }, assertFn: noChange, }, { name: "add on too new derived (even if parent-hash looks correct)", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(fDerived, dDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(fDerived, dDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, { name: "add on old derived (even if parent-hash looks correct)", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(cDerived, bDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(cDerived, bDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, { name: "add on even older derived", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(bDerived, aDerived.Hash)), entrydb.ErrOutOfOrder) + require.ErrorIs(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(bDerived, aDerived.Hash)), types.ErrOutOfOrder) }, assertFn: noChange, }, @@ -159,7 +158,7 @@ func TestBadUpdates(t *testing.T) { name: "repeat self, silent no-op", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { pre := m.DBDerivedEntryCount - require.NoError(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), entrydb.ErrOutOfOrder) + require.NoError(t, db.AddDerived(toRef(dDerivedFrom, cDerivedFrom.Hash), toRef(dDerived, cDerived.Hash)), types.ErrOutOfOrder) require.Equal(t, pre, m.DBDerivedEntryCount) }, assertFn: noChange, diff --git a/op-supervisor/supervisor/backend/db/logs/db.go b/op-supervisor/supervisor/backend/db/logs/db.go index 009499755059..dc98b7472074 100644 --- a/op-supervisor/supervisor/backend/db/logs/db.go +++ b/op-supervisor/supervisor/backend/db/logs/db.go @@ -139,8 +139,8 @@ func (db *DB) FindSealedBlock(number uint64) (seal types.BlockSeal, err error) { db.rwLock.RLock() defer db.rwLock.RUnlock() iter, err := db.newIteratorAt(number, 0) - if errors.Is(err, entrydb.ErrFuture) { - return types.BlockSeal{}, fmt.Errorf("block %d is not known yet: %w", number, entrydb.ErrFuture) + if errors.Is(err, types.ErrFuture) { + return types.BlockSeal{}, fmt.Errorf("block %d is not known yet: %w", number, types.ErrFuture) } else if err != nil { return types.BlockSeal{}, fmt.Errorf("failed to find sealed block %d: %w", number, err) } @@ -162,6 +162,89 @@ func (db *DB) FindSealedBlock(number uint64) (seal types.BlockSeal, err error) { }, nil } +// StartingBlock returns the first block seal in the DB, if any. +func (db *DB) StartingBlock() (seal types.BlockSeal, err error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + iter := db.newIterator(0) + if err := iter.NextBlock(); err != nil { + return types.BlockSeal{}, err + } + h, n, _ := iter.SealedBlock() + t, _ := iter.SealedTimestamp() + return types.BlockSeal{ + Hash: h, + Number: n, + Timestamp: t, + }, err +} + +// OpenBlock returns the Executing Messages for the block at the given number. +// it returns identification of the block, the parent block, and the executing messages. +func (db *DB) OpenBlock(blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, retErr error) { + db.rwLock.RLock() + defer db.rwLock.RUnlock() + + if blockNum == 0 { + seal, err := db.StartingBlock() + if err != nil { + retErr = err + return + } + ref = eth.BlockRef{ + Hash: seal.Hash, + Number: seal.Number, + ParentHash: common.Hash{}, + Time: seal.Timestamp, + } + logCount = 0 + execMsgs = nil + return + } + + // start at the first log (if any) after the block-seal of the parent block + blockIter, err := db.newIteratorAt(blockNum-1, 0) + if err != nil { + retErr = err + return + } + // register the parent block + parentHash, _, ok := blockIter.SealedBlock() + if ok { + ref.ParentHash = parentHash + } + // walk to the end of the block, and remember what we see in the block. + logCount = 0 + execMsgs = make(map[uint32]*types.ExecutingMessage, 0) + retErr = blockIter.TraverseConditional(func(state IteratorState) error { + _, logIndex, ok := state.InitMessage() + if ok { + logCount = logIndex + 1 + } + if m := state.ExecMessage(); m != nil { + execMsgs[logIndex] = m + } + h, n, ok := state.SealedBlock() + if !ok { + return nil + } + if n == blockNum { + ref.Number = n + ref.Hash = h + ref.Time, _ = state.SealedTimestamp() + return types.ErrStop + } + if n > blockNum { + return fmt.Errorf("expected to run into block %d, but did not find it, found %d: %w", blockNum, n, types.ErrDataCorruption) + } + return nil + }) + if errors.Is(retErr, types.ErrStop) { + retErr = nil + } + return +} + // LatestSealedBlockNum returns the block number of the block that was last sealed, // or ok=false if there is no sealed block (i.e. empty DB) func (db *DB) LatestSealedBlockNum() (n uint64, ok bool) { @@ -213,21 +296,21 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ return nil } if n == blockNum { - return entrydb.ErrStop + return types.ErrStop } if n > blockNum { - return entrydb.ErrDataCorruption + return types.ErrDataCorruption } return nil }) if err == nil { panic("expected iterator to stop with error") } - if errors.Is(err, entrydb.ErrFuture) { + if errors.Is(err, types.ErrFuture) { // Log is known, but as part of an unsealed block. return types.BlockSeal{}, nil } - if errors.Is(err, entrydb.ErrStop) { + if errors.Is(err, types.ErrStop) { h, n, _ := iter.SealedBlock() timestamp, _ := iter.SealedTimestamp() return types.BlockSeal{ @@ -241,12 +324,12 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ func (db *DB) findLogInfo(blockNum uint64, logIdx uint32) (common.Hash, Iterator, error) { if blockNum == 0 { - return common.Hash{}, nil, entrydb.ErrConflict // no logs in block 0 + return common.Hash{}, nil, types.ErrConflict // no logs in block 0 } // blockNum-1, such that we find a log that came after the parent num-1 was sealed. // logIdx, such that all entries before logIdx can be skipped, but logIdx itself is still readable. iter, err := db.newIteratorAt(blockNum-1, logIdx) - if errors.Is(err, entrydb.ErrFuture) { + if errors.Is(err, types.ErrFuture) { db.log.Trace("Could not find log yet", "blockNum", blockNum, "logIdx", logIdx) return common.Hash{}, nil, err } else if err != nil { @@ -261,7 +344,7 @@ func (db *DB) findLogInfo(blockNum uint64, logIdx uint32) (common.Hash, Iterator } else if x < blockNum-1 { panic(fmt.Errorf("bug in newIteratorAt, expected to have found parent block %d but got %d", blockNum-1, x)) } else if x > blockNum-1 { - return common.Hash{}, nil, fmt.Errorf("log does not exist, found next block already: %w", entrydb.ErrConflict) + return common.Hash{}, nil, fmt.Errorf("log does not exist, found next block already: %w", types.ErrConflict) } logHash, x, ok := iter.InitMessage() if !ok { @@ -282,7 +365,7 @@ func (db *DB) newIteratorAt(blockNum uint64, logIndex uint32) (*iterator, error) searchCheckpointIndex, err := db.searchCheckpoint(blockNum, logIndex) if errors.Is(err, io.EOF) { // Did not find a checkpoint to start reading from so the log cannot be present. - return nil, entrydb.ErrFuture + return nil, types.ErrFuture } else if err != nil { return nil, err } @@ -298,9 +381,9 @@ func (db *DB) newIteratorAt(blockNum uint64, logIndex uint32) (*iterator, error) if _, n, ok := iter.SealedBlock(); ok && n == blockNum { // we may already have it exactly break } - if err := iter.NextBlock(); errors.Is(err, entrydb.ErrFuture) { + if err := iter.NextBlock(); errors.Is(err, types.ErrFuture) { db.log.Trace("ran out of data, could not find block", "nextIndex", iter.NextIndex(), "target", blockNum) - return nil, entrydb.ErrFuture + return nil, types.ErrFuture } else if err != nil { db.log.Error("failed to read next block", "nextIndex", iter.NextIndex(), "target", blockNum, "err", err) return nil, err @@ -314,7 +397,7 @@ func (db *DB) newIteratorAt(blockNum uint64, logIndex uint32) (*iterator, error) continue } if num != blockNum { // block does not contain - return nil, fmt.Errorf("looking for %d, but already at %d: %w", blockNum, num, entrydb.ErrConflict) + return nil, fmt.Errorf("looking for %d, but already at %d: %w", blockNum, num, types.ErrConflict) } break } @@ -323,7 +406,7 @@ func (db *DB) newIteratorAt(blockNum uint64, logIndex uint32) (*iterator, error) // so two logs before quiting (and not 3 to then quit after). for iter.current.logsSince < logIndex { if err := iter.NextInitMsg(); err == io.EOF { - return nil, entrydb.ErrFuture + return nil, types.ErrFuture } else if err != nil { return nil, err } @@ -333,7 +416,7 @@ func (db *DB) newIteratorAt(blockNum uint64, logIndex uint32) (*iterator, error) } if num > blockNum { // we overshot, the block did not contain as many seen log events as requested - return nil, entrydb.ErrConflict + return nil, types.ErrConflict } _, idx, ok := iter.InitMessage() if !ok { @@ -367,7 +450,7 @@ func (db *DB) newIterator(index entrydb.EntryIdx) *iterator { // Returns the index of the searchCheckpoint to begin reading from or an error. func (db *DB) searchCheckpoint(sealedBlockNum uint64, logsSince uint32) (entrydb.EntryIdx, error) { if db.lastEntryContext.nextEntryIndex == 0 { - return 0, entrydb.ErrFuture // empty DB, everything is in the future + return 0, types.ErrFuture // empty DB, everything is in the future } n := (db.lastEntryIdx() / searchCheckpointFrequency) + 1 // Define: x is the array of known checkpoints @@ -404,7 +487,7 @@ func (db *DB) searchCheckpoint(sealedBlockNum uint64, logsSince uint32) (entrydb if checkpoint.blockNum > sealedBlockNum || (checkpoint.blockNum == sealedBlockNum && checkpoint.logsSince > logsSince) { return 0, fmt.Errorf("missing data, earliest search checkpoint is %d with %d logs, cannot find something before or at %d with %d logs: %w", - checkpoint.blockNum, checkpoint.logsSince, sealedBlockNum, logsSince, entrydb.ErrSkipped) + checkpoint.blockNum, checkpoint.logsSince, sealedBlockNum, logsSince, types.ErrSkipped) } return result, nil } diff --git a/op-supervisor/supervisor/backend/db/logs/db_test.go b/op-supervisor/supervisor/backend/db/logs/db_test.go index 718bc68a04f4..9e7e9c51a68f 100644 --- a/op-supervisor/supervisor/backend/db/logs/db_test.go +++ b/op-supervisor/supervisor/backend/db/logs/db_test.go @@ -89,8 +89,8 @@ func TestLatestSealedBlockNum(t *testing.T) { require.False(t, ok, "empty db expected") require.Zero(t, n) idx, err := db.searchCheckpoint(0, 0) - require.ErrorIs(t, err, entrydb.ErrFuture, "no checkpoint in empty db") - require.ErrorIs(t, err, entrydb.ErrFuture, "no checkpoint in empty db") + require.ErrorIs(t, err, types.ErrFuture, "no checkpoint in empty db") + require.ErrorIs(t, err, types.ErrFuture, "no checkpoint in empty db") require.Zero(t, idx) }) }) @@ -107,6 +107,14 @@ func TestLatestSealedBlockNum(t *testing.T) { idx, err := db.searchCheckpoint(0, 0) require.NoError(t, err) require.Zero(t, idx, "genesis block as checkpoint 0") + + // Test if we can open the genesis block + ref, logCount, execMsgs, err := db.OpenBlock(0) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Zero(t, logCount) + require.Equal(t, genesis, ref.ID()) + require.Equal(t, uint64(5000), ref.Time) }) }) t.Run("Later genesis case", func(t *testing.T) { @@ -123,8 +131,16 @@ func TestLatestSealedBlockNum(t *testing.T) { require.NoError(t, err) require.Zero(t, idx, "anchor block as checkpoint 0") _, err = db.searchCheckpoint(0, 0) - require.ErrorIs(t, err, entrydb.ErrSkipped, "no checkpoint before genesis") - require.ErrorIs(t, err, entrydb.ErrSkipped, "no checkpoint before genesis") + require.ErrorIs(t, err, types.ErrSkipped, "no checkpoint before genesis") + require.ErrorIs(t, err, types.ErrSkipped, "no checkpoint before genesis") + + // Test if we can open the starting block + _, _, _, err = db.OpenBlock(genesis.Number) + // no data to find the parent-hash. + // OpenBlock cannot start from the first entry, when not 0. + // To start at a non-zero block, index the seal of the parent-block block before it, + // and then that parent-hash will be available. + require.ErrorIs(t, err, types.ErrSkipped) }) }) t.Run("Block 1 case", func(t *testing.T) { @@ -142,6 +158,22 @@ func TestLatestSealedBlockNum(t *testing.T) { idx, err := db.searchCheckpoint(block1.Number, 0) require.NoError(t, err) require.Equal(t, entrydb.EntryIdx(0), idx, "checkpoint 0 still for block 1") + + // Test if we can open the starting block + ref, logCount, execMsgs, err := db.OpenBlock(genesis.Number) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Zero(t, logCount) + require.Equal(t, genesis, ref.ID()) + require.Equal(t, uint64(5000), ref.Time) + + // Test if we can open the first block after genesis + ref, logCount, execMsgs, err = db.OpenBlock(block1.Number) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Zero(t, logCount) + require.Equal(t, block1, ref.ID()) + require.Equal(t, uint64(5001), ref.Time) }) }) t.Run("Using checkpoint case", func(t *testing.T) { @@ -151,7 +183,7 @@ func TestLatestSealedBlockNum(t *testing.T) { require.NoError(t, db.SealBlock(common.Hash{}, genesis, 5000), "seal genesis") for i := 1; i <= 260; i++ { id := eth.BlockID{Hash: createHash(i), Number: uint64(i)} - require.NoError(t, db.SealBlock(createHash(i-1), id, 5001), "seal block %d", i) + require.NoError(t, db.SealBlock(createHash(i-1), id, 5000+uint64(i)), "seal block %d", i) } }, func(t *testing.T, db *DB, m *stubMetrics) { @@ -164,6 +196,14 @@ func TestLatestSealedBlockNum(t *testing.T) { // It costs 2 entries per block, so if we add more than 1 checkpoint worth of blocks, // then we get to checkpoint 2 require.Equal(t, entrydb.EntryIdx(searchCheckpointFrequency*2), idx, "checkpoint 1 reached") + + // Test if we can open the block + ref, logCount, execMsgs, err := db.OpenBlock(n) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Zero(t, logCount) + require.Equal(t, createHash(int(n)), ref.Hash) + require.Equal(t, uint64(5000)+n, ref.Time) }) }) } @@ -176,8 +216,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { genesis := eth.BlockID{Hash: createHash(15), Number: 0} err := db.AddLog(createHash(1), genesis, 0, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -192,6 +232,12 @@ func TestAddLog(t *testing.T) { }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 16, 0, createHash(1)) + + ref, logCount, execMsgs, err := db.OpenBlock(16) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Equal(t, uint32(1), logCount) + require.Equal(t, eth.BlockRef{Hash: createHash(16), Number: 16, ParentHash: createHash(15), Time: 5001}, ref) }) }) @@ -221,6 +267,18 @@ func TestAddLog(t *testing.T) { requireContains(t, db, 16, 0, createHash(1)) requireContains(t, db, 16, 1, createHash(2)) requireContains(t, db, 16, 2, createHash(3)) + + ref, logCount, execMsgs, err := db.OpenBlock(13) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Equal(t, uint32(0), logCount) + require.Equal(t, eth.BlockRef{Hash: createHash(13), Number: 13, ParentHash: createHash(12), Time: 5013}, ref) + + ref, logCount, execMsgs, err = db.OpenBlock(16) + require.NoError(t, err) + require.Empty(t, execMsgs) + require.Equal(t, uint32(3), logCount) + require.Equal(t, eth.BlockRef{Hash: createHash(16), Number: 16, ParentHash: createHash(15), Time: 5016}, ref) }) }) @@ -267,8 +325,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl14 := eth.BlockID{Hash: createHash(14), Number: 14} err := db.SealBlock(createHash(13), bl14, 5000) - require.ErrorIs(t, err, entrydb.ErrConflict) - require.ErrorIs(t, err, entrydb.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) + require.ErrorIs(t, err, types.ErrConflict) }) }) @@ -285,8 +343,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { onto := eth.BlockID{Hash: createHash(14), Number: 14} err := db.AddLog(createHash(1), onto, 0, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "cannot build logs on 14 when 15 is already sealed") - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "cannot build logs on 14 when 15 is already sealed") + require.ErrorIs(t, err, types.ErrOutOfOrder, "cannot build logs on 14 when 15 is already sealed") + require.ErrorIs(t, err, types.ErrOutOfOrder, "cannot build logs on 14 when 15 is already sealed") }) }) @@ -302,8 +360,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl15 := eth.BlockID{Hash: createHash(15), Number: 15} err := db.AddLog(createHash(1), bl15, 0, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "already at log index 2") - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "already at log index 2") + require.ErrorIs(t, err, types.ErrOutOfOrder, "already at log index 2") + require.ErrorIs(t, err, types.ErrOutOfOrder, "already at log index 2") }) }) @@ -318,8 +376,8 @@ func TestAddLog(t *testing.T) { }, func(t *testing.T, db *DB, m *stubMetrics) { err := db.AddLog(createHash(1), eth.BlockID{Hash: createHash(16), Number: 16}, 0, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -335,8 +393,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl15 := eth.BlockID{Hash: createHash(15), Number: 15} err := db.AddLog(createHash(1), bl15, 1, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "already at log index 2") - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "already at log index 2") + require.ErrorIs(t, err, types.ErrOutOfOrder, "already at log index 2") + require.ErrorIs(t, err, types.ErrOutOfOrder, "already at log index 2") }) }) @@ -352,8 +410,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl15 := eth.BlockID{Hash: createHash(16), Number: 16} err := db.AddLog(createHash(1), bl15, 2, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -368,8 +426,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl15 := eth.BlockID{Hash: createHash(15), Number: 15} err := db.AddLog(createHash(1), bl15, 2, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -382,8 +440,8 @@ func TestAddLog(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { bl15 := eth.BlockID{Hash: createHash(15), Number: 15} err := db.AddLog(createHash(1), bl15, 5, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -404,8 +462,8 @@ func TestAddLog(t *testing.T) { err = db.SealBlock(bl15.Hash, bl16, 5001) require.NoError(t, err) err = db.AddLog(createHash(1), bl16, 1, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) }) }) @@ -701,6 +759,14 @@ func TestExecutes(t *testing.T) { // 51 only contained 3 logs, not 4 requireConflicts(t, db, 51, 3, createHash(2)) + + // 51 contains an executing message, and 2 other non-executing logs + ref, logCount, execMsgs, err := db.OpenBlock(51) + require.NoError(t, err) + require.Len(t, execMsgs, 1) + require.Equal(t, &execMsg1, execMsgs[1]) + require.Equal(t, uint32(3), logCount) + require.Equal(t, eth.BlockRef{Hash: createHash(51), Number: 51, ParentHash: createHash(50), Time: 5001}, ref) }) } @@ -710,9 +776,9 @@ func TestGetBlockInfo(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) {}, func(t *testing.T, db *DB, m *stubMetrics) { _, err := db.FindSealedBlock(10) - require.ErrorIs(t, err, entrydb.ErrFuture) + require.ErrorIs(t, err, types.ErrFuture) _, err = db.FindSealedBlock(10) - require.ErrorIs(t, err, entrydb.ErrFuture) + require.ErrorIs(t, err, types.ErrFuture) }) }) @@ -727,9 +793,9 @@ func TestGetBlockInfo(t *testing.T) { func(t *testing.T, db *DB, m *stubMetrics) { // if the DB starts at 11, then shouldn't find 10 _, err := db.FindSealedBlock(10) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) _, err = db.FindSealedBlock(10) - require.ErrorIs(t, err, entrydb.ErrSkipped) + require.ErrorIs(t, err, types.ErrSkipped) }) }) @@ -772,8 +838,8 @@ func requireConflicts(t *testing.T, db *DB, blockNum uint64, logIdx uint32, logH m, ok := db.m.(*stubMetrics) require.True(t, ok, "Did not get the expected metrics type") _, err := db.Contains(blockNum, logIdx, logHash) - require.ErrorIs(t, err, entrydb.ErrConflict, "canonical chain must not include this log") - require.ErrorIs(t, err, entrydb.ErrConflict, "canonical chain must not include this log") + require.ErrorIs(t, err, types.ErrConflict, "canonical chain must not include this log") + require.ErrorIs(t, err, types.ErrConflict, "canonical chain must not include this log") require.LessOrEqual(t, m.entriesReadForSearch, int64(searchCheckpointFrequency*2), "Should not need to read more than between two checkpoints") } @@ -781,8 +847,8 @@ func requireFuture(t *testing.T, db *DB, blockNum uint64, logIdx uint32, logHash m, ok := db.m.(*stubMetrics) require.True(t, ok, "Did not get the expected metrics type") _, err := db.Contains(blockNum, logIdx, logHash) - require.ErrorIs(t, err, entrydb.ErrFuture, "canonical chain does not yet include this log") - require.ErrorIs(t, err, entrydb.ErrFuture, "canonical chain does not yet include this log") + require.ErrorIs(t, err, types.ErrFuture, "canonical chain does not yet include this log") + require.ErrorIs(t, err, types.ErrFuture, "canonical chain does not yet include this log") require.LessOrEqual(t, m.entriesReadForSearch, int64(searchCheckpointFrequency*2), "Should not need to read more than between two checkpoints") } @@ -943,11 +1009,11 @@ func TestRewind(t *testing.T) { t.Run("WhenEmpty", func(t *testing.T) { runDBTest(t, func(t *testing.T, db *DB, m *stubMetrics) {}, func(t *testing.T, db *DB, m *stubMetrics) { - require.ErrorIs(t, db.Rewind(100), entrydb.ErrFuture) - require.ErrorIs(t, db.Rewind(100), entrydb.ErrFuture) + require.ErrorIs(t, db.Rewind(100), types.ErrFuture) + require.ErrorIs(t, db.Rewind(100), types.ErrFuture) // Genesis is a block to, not present in an empty DB - require.ErrorIs(t, db.Rewind(0), entrydb.ErrFuture) - require.ErrorIs(t, db.Rewind(0), entrydb.ErrFuture) + require.ErrorIs(t, db.Rewind(0), types.ErrFuture) + require.ErrorIs(t, db.Rewind(0), types.ErrFuture) }) }) @@ -965,8 +1031,8 @@ func TestRewind(t *testing.T) { require.NoError(t, db.SealBlock(bl51.Hash, bl52, 504)) require.NoError(t, db.AddLog(createHash(4), bl52, 0, nil)) // cannot rewind to a block that is not sealed yet - require.ErrorIs(t, db.Rewind(53), entrydb.ErrFuture) - require.ErrorIs(t, db.Rewind(53), entrydb.ErrFuture) + require.ErrorIs(t, db.Rewind(53), types.ErrFuture) + require.ErrorIs(t, db.Rewind(53), types.ErrFuture) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 51, 0, createHash(1)) @@ -985,8 +1051,8 @@ func TestRewind(t *testing.T) { require.NoError(t, db.AddLog(createHash(1), bl50, 0, nil)) require.NoError(t, db.AddLog(createHash(2), bl50, 1, nil)) // cannot go back to an unknown block - require.ErrorIs(t, db.Rewind(25), entrydb.ErrSkipped) - require.ErrorIs(t, db.Rewind(25), entrydb.ErrSkipped) + require.ErrorIs(t, db.Rewind(25), types.ErrSkipped) + require.ErrorIs(t, db.Rewind(25), types.ErrSkipped) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 51, 0, createHash(1)) @@ -1111,14 +1177,14 @@ func TestRewind(t *testing.T) { bl29 := eth.BlockID{Hash: createHash(29), Number: 29} // 29 was deleted err := db.AddLog(createHash(2), bl29, 1, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "Cannot add log on removed block") - require.ErrorIs(t, err, entrydb.ErrOutOfOrder, "Cannot add log on removed block") + require.ErrorIs(t, err, types.ErrOutOfOrder, "Cannot add log on removed block") + require.ErrorIs(t, err, types.ErrOutOfOrder, "Cannot add log on removed block") // 15 is older, we have up to 16 bl15 := eth.BlockID{Hash: createHash(15), Number: 15} // try to add a third log to 15 err = db.AddLog(createHash(10), bl15, 2, nil) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) - require.ErrorIs(t, err, entrydb.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) + require.ErrorIs(t, err, types.ErrOutOfOrder) bl16 := eth.BlockID{Hash: createHash(16), Number: 16} // try to add a log to 17, on top of 16 err = db.AddLog(createHash(42), bl16, 0, nil) diff --git a/op-supervisor/supervisor/backend/db/logs/entries.go b/op-supervisor/supervisor/backend/db/logs/entries.go index acb0d2b55626..5c5472c522b7 100644 --- a/op-supervisor/supervisor/backend/db/logs/entries.go +++ b/op-supervisor/supervisor/backend/db/logs/entries.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -29,7 +28,7 @@ func newSearchCheckpoint(blockNum uint64, logsSince uint32, timestamp uint64) se func newSearchCheckpointFromEntry(data Entry) (searchCheckpoint, error) { if data.Type() != TypeSearchCheckpoint { - return searchCheckpoint{}, fmt.Errorf("%w: attempting to decode search checkpoint but was type %s", entrydb.ErrDataCorruption, data.Type()) + return searchCheckpoint{}, fmt.Errorf("%w: attempting to decode search checkpoint but was type %s", types.ErrDataCorruption, data.Type()) } return searchCheckpoint{ blockNum: binary.LittleEndian.Uint64(data[1:9]), @@ -59,7 +58,7 @@ func newCanonicalHash(hash common.Hash) canonicalHash { func newCanonicalHashFromEntry(data Entry) (canonicalHash, error) { if data.Type() != TypeCanonicalHash { - return canonicalHash{}, fmt.Errorf("%w: attempting to decode canonical hash but was type %s", entrydb.ErrDataCorruption, data.Type()) + return canonicalHash{}, fmt.Errorf("%w: attempting to decode canonical hash but was type %s", types.ErrDataCorruption, data.Type()) } return newCanonicalHash(common.Hash(data[1:33])), nil } @@ -78,7 +77,7 @@ type initiatingEvent struct { func newInitiatingEventFromEntry(data Entry) (initiatingEvent, error) { if data.Type() != TypeInitiatingEvent { - return initiatingEvent{}, fmt.Errorf("%w: attempting to decode initiating event but was type %s", entrydb.ErrDataCorruption, data.Type()) + return initiatingEvent{}, fmt.Errorf("%w: attempting to decode initiating event but was type %s", types.ErrDataCorruption, data.Type()) } flags := data[1] return initiatingEvent{ @@ -109,7 +108,7 @@ func (i initiatingEvent) encode() Entry { } type executingLink struct { - chain uint32 + chain uint32 // chain index, not a chain ID blockNum uint64 logIdx uint32 timestamp uint64 @@ -120,7 +119,7 @@ func newExecutingLink(msg types.ExecutingMessage) (executingLink, error) { return executingLink{}, fmt.Errorf("log idx is too large (%v)", msg.LogIdx) } return executingLink{ - chain: msg.Chain, + chain: uint32(msg.Chain), blockNum: msg.BlockNum, logIdx: msg.LogIdx, timestamp: msg.Timestamp, @@ -129,7 +128,7 @@ func newExecutingLink(msg types.ExecutingMessage) (executingLink, error) { func newExecutingLinkFromEntry(data Entry) (executingLink, error) { if data.Type() != TypeExecutingLink { - return executingLink{}, fmt.Errorf("%w: attempting to decode executing link but was type %s", entrydb.ErrDataCorruption, data.Type()) + return executingLink{}, fmt.Errorf("%w: attempting to decode executing link but was type %s", types.ErrDataCorruption, data.Type()) } timestamp := binary.LittleEndian.Uint64(data[16:24]) return executingLink{ @@ -166,7 +165,7 @@ func newExecutingCheck(hash common.Hash) executingCheck { func newExecutingCheckFromEntry(data Entry) (executingCheck, error) { if data.Type() != TypeExecutingCheck { - return executingCheck{}, fmt.Errorf("%w: attempting to decode executing check but was type %s", entrydb.ErrDataCorruption, data.Type()) + return executingCheck{}, fmt.Errorf("%w: attempting to decode executing check but was type %s", types.ErrDataCorruption, data.Type()) } return newExecutingCheck(common.Hash(data[1:33])), nil } diff --git a/op-supervisor/supervisor/backend/db/logs/iterator.go b/op-supervisor/supervisor/backend/db/logs/iterator.go index 5a7015ca4fda..c107a2a9fb86 100644 --- a/op-supervisor/supervisor/backend/db/logs/iterator.go +++ b/op-supervisor/supervisor/backend/db/logs/iterator.go @@ -41,7 +41,7 @@ type traverseConditionalFn func(state IteratorState) error func (i *iterator) End() error { for { _, err := i.next() - if errors.Is(err, entrydb.ErrFuture) { + if errors.Is(err, types.ErrFuture) { return nil } else if err != nil { return err @@ -49,7 +49,7 @@ func (i *iterator) End() error { } } -// NextInitMsg returns the next initiating message in the iterator. +// NextInitMsg advances the iterator until it reads the next Initiating Message into the current state. // It scans forward until it finds and fully reads an initiating event, skipping any blocks. func (i *iterator) NextInitMsg() error { seenLog := false @@ -73,9 +73,8 @@ func (i *iterator) NextInitMsg() error { } } -// NextExecMsg returns the next executing message in the iterator. +// NextExecMsg advances the iterator until it reads the next Executing Message into the current state. // It scans forward until it finds and fully reads an initiating event, skipping any blocks. -// This does not stay at the executing message of the current initiating message, if there is any. func (i *iterator) NextExecMsg() error { for { err := i.NextInitMsg() @@ -88,7 +87,7 @@ func (i *iterator) NextExecMsg() error { } } -// NextBlock returns the next block in the iterator. +// NextBlock advances the iterator until it reads the next block into the current state. // It scans forward until it finds and fully reads a block, skipping any events. func (i *iterator) NextBlock() error { seenBlock := false @@ -134,7 +133,7 @@ func (i *iterator) next() (EntryType, error) { entry, err := i.db.store.Read(index) if err != nil { if errors.Is(err, io.EOF) { - return 0, entrydb.ErrFuture + return 0, types.ErrFuture } return 0, fmt.Errorf("failed to read entry %d: %w", index, err) } diff --git a/op-supervisor/supervisor/backend/db/logs/state.go b/op-supervisor/supervisor/backend/db/logs/state.go index 2e51295c93df..3c90102b1ad0 100644 --- a/op-supervisor/supervisor/backend/db/logs/state.go +++ b/op-supervisor/supervisor/backend/db/logs/state.go @@ -200,7 +200,7 @@ func (l *logContext) processEntry(entry Entry) error { return err } l.execMsg = &types.ExecutingMessage{ - Chain: link.chain, + Chain: types.ChainIndex(link.chain), // TODO(#11105): translate chain ID to chain index BlockNum: link.blockNum, LogIdx: link.logIdx, Timestamp: link.timestamp, @@ -352,13 +352,13 @@ func (l *logContext) SealBlock(parent common.Hash, upd eth.BlockID, timestamp ui return err } if l.blockHash != parent { - return fmt.Errorf("%w: cannot apply block %s (parent %s) on top of %s", entrydb.ErrConflict, upd, parent, l.blockHash) + return fmt.Errorf("%w: cannot apply block %s (parent %s) on top of %s", types.ErrConflict, upd, parent, l.blockHash) } if l.blockHash != (common.Hash{}) && l.blockNum+1 != upd.Number { - return fmt.Errorf("%w: cannot apply block %d on top of %d", entrydb.ErrConflict, upd.Number, l.blockNum) + return fmt.Errorf("%w: cannot apply block %d on top of %d", types.ErrConflict, upd.Number, l.blockNum) } if l.timestamp > timestamp { - return fmt.Errorf("%w: block timestamp %d must be equal or larger than current timestamp %d", entrydb.ErrConflict, timestamp, l.timestamp) + return fmt.Errorf("%w: block timestamp %d must be equal or larger than current timestamp %d", types.ErrConflict, timestamp, l.timestamp) } } l.blockHash = upd.Hash @@ -375,28 +375,28 @@ func (l *logContext) SealBlock(parent common.Hash, upd eth.BlockID, timestamp ui // The parent-block that the log comes after must be applied with ApplyBlock first. func (l *logContext) ApplyLog(parentBlock eth.BlockID, logIdx uint32, logHash common.Hash, execMsg *types.ExecutingMessage) error { if parentBlock == (eth.BlockID{}) { - return fmt.Errorf("genesis does not have logs: %w", entrydb.ErrOutOfOrder) + return fmt.Errorf("genesis does not have logs: %w", types.ErrOutOfOrder) } if err := l.inferFull(); err != nil { // ensure we can start applying return err } if !l.hasCompleteBlock() { if l.blockNum == 0 { - return fmt.Errorf("%w: should not have logs in block 0", entrydb.ErrOutOfOrder) + return fmt.Errorf("%w: should not have logs in block 0", types.ErrOutOfOrder) } else { return errors.New("cannot append log before last known block is sealed") } } // check parent block if l.blockHash != parentBlock.Hash { - return fmt.Errorf("%w: log builds on top of block %s, but have block %s", entrydb.ErrOutOfOrder, parentBlock, l.blockHash) + return fmt.Errorf("%w: log builds on top of block %s, but have block %s", types.ErrOutOfOrder, parentBlock, l.blockHash) } if l.blockNum != parentBlock.Number { - return fmt.Errorf("%w: log builds on top of block %d, but have block %d", entrydb.ErrOutOfOrder, parentBlock.Number, l.blockNum) + return fmt.Errorf("%w: log builds on top of block %d, but have block %d", types.ErrOutOfOrder, parentBlock.Number, l.blockNum) } // check if log fits on top. The length so far == the index of the next log. if logIdx != l.logsSince { - return fmt.Errorf("%w: expected event index %d, cannot append %d", entrydb.ErrOutOfOrder, l.logsSince, logIdx) + return fmt.Errorf("%w: expected event index %d, cannot append %d", types.ErrOutOfOrder, l.logsSince, logIdx) } l.logHash = logHash l.execMsg = execMsg diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index c331379f9a28..5db821396c8a 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/logs" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -18,7 +17,7 @@ func (db *ChainsDB) FindSealedBlock(chain types.ChainID, number uint64) (seal ty logDB, ok := db.logDBs[chain] if !ok { - return types.BlockSeal{}, fmt.Errorf("%w: %v", ErrUnknownChain, chain) + return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } return logDB.FindSealedBlock(number) } @@ -37,17 +36,69 @@ func (db *ChainsDB) LatestBlockNum(chain types.ChainID) (num uint64, ok bool) { return logDB.LatestSealedBlockNum() } +func (db *ChainsDB) IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error { + db.mu.RLock() + defer db.mu.RUnlock() + v, ok := db.crossUnsafe[chainID] + if !ok { + return types.ErrUnknownChain + } + if v == (types.BlockSeal{}) { + return types.ErrFuture + } + if block.Number > v.Number { + return types.ErrFuture + } + // TODO(#11693): make cross-unsafe reorg safe + return nil +} + +func (db *ChainsDB) ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (parent eth.BlockID, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + logDB, ok := db.logDBs[chainID] + if !ok { + return eth.BlockID{}, types.ErrUnknownChain + } + if parentOf.Number == 0 { + return eth.BlockID{}, nil + } + // TODO(#11693): make parent-lookup reorg safe + got, err := logDB.FindSealedBlock(parentOf.Number - 1) + if err != nil { + return eth.BlockID{}, err + } + return got.ID(), nil +} + +func (db *ChainsDB) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error { + db.mu.RLock() + defer db.mu.RUnlock() + logDB, ok := db.logDBs[chainID] + if !ok { + return types.ErrUnknownChain + } + got, err := logDB.FindSealedBlock(block.Number) + if err != nil { + return err + } + if got.ID() != block { + return fmt.Errorf("found %s but was looking for unsafe block %s: %w", got, block, types.ErrConflict) + } + return nil +} + func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) { db.mu.RLock() defer db.mu.RUnlock() eventsDB, ok := db.logDBs[chainID] if !ok { - return types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.ErrUnknownChain } n, ok := eventsDB.LatestSealedBlockNum() if !ok { - return types.BlockSeal{}, entrydb.ErrFuture + return types.BlockSeal{}, types.ErrFuture } return eventsDB.FindSealedBlock(n) } @@ -58,7 +109,7 @@ func (db *ChainsDB) CrossUnsafe(chainID types.ChainID) (types.BlockSeal, error) result, ok := db.crossUnsafe[chainID] if !ok { - return types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.ErrUnknownChain } // Fall back to cross-safe if cross-unsafe is not known yet if result == (types.BlockSeal{}) { @@ -77,7 +128,7 @@ func (db *ChainsDB) LocalSafe(chainID types.ChainID) (derivedFrom types.BlockSea localDB, ok := db.localDBs[chainID] if !ok { - return types.BlockSeal{}, types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain } return localDB.Latest() } @@ -88,7 +139,7 @@ func (db *ChainsDB) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSea crossDB, ok := db.crossDBs[chainID] if !ok { - return types.BlockSeal{}, types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain } return crossDB.Latest() } @@ -111,7 +162,7 @@ func (db *ChainsDB) Finalized(chainID types.ChainID) (types.BlockSeal, error) { func (db *ChainsDB) LastDerivedFrom(chainID types.ChainID, derivedFrom eth.BlockID) (derived types.BlockSeal, err error) { crossDB, ok := db.crossDBs[chainID] if !ok { - return types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.ErrUnknownChain } return crossDB.LastDerivedAt(derivedFrom) } @@ -122,7 +173,7 @@ func (db *ChainsDB) DerivedFrom(chainID types.ChainID, derived eth.BlockID) (der localDB, ok := db.localDBs[chainID] if !ok { - return types.BlockSeal{}, ErrUnknownChain + return types.BlockSeal{}, types.ErrUnknownChain } return localDB.DerivedFrom(derived) } @@ -135,11 +186,168 @@ func (db *ChainsDB) Check(chain types.ChainID, blockNum uint64, logIdx uint32, l logDB, ok := db.logDBs[chain] if !ok { - return types.BlockSeal{}, fmt.Errorf("%w: %v", ErrUnknownChain, chain) + return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } return logDB.Contains(blockNum, logIdx, logHash) } +// OpenBlock returns the Executing Messages for the block at the given number on the given chain. +// it routes the request to the appropriate logDB. +func (db *ChainsDB) OpenBlock(chainID types.ChainID, blockNum uint64) (seal eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + + logDB, ok := db.logDBs[chainID] + if !ok { + return eth.BlockRef{}, 0, nil, types.ErrUnknownChain + } + return logDB.OpenBlock(blockNum) +} + +// LocalDerivedFrom returns the block that the given block was derived from, if it exists in the local derived-from storage. +// it routes the request to the appropriate localDB. +func (db *ChainsDB) LocalDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + + lDB, ok := db.localDBs[chain] + if !ok { + return types.BlockSeal{}, types.ErrUnknownChain + } + return lDB.DerivedFrom(derived) +} + +// CrossDerivedFrom returns the block that the given block was derived from, if it exists in the cross derived-from storage. +// it routes the request to the appropriate crossDB. +func (db *ChainsDB) CrossDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + + xDB, ok := db.crossDBs[chain] + if !ok { + return types.BlockSeal{}, types.ErrUnknownChain + } + return xDB.DerivedFrom(derived) +} + +// CandidateCrossSafe returns the candidate local-safe block that may become cross-safe. +// +// This returns ErrFuture if no block is known yet. +// +// Or ErrConflict if there is an inconsistency between the local-safe and cross-safe DB. +// +// Or ErrOutOfScope, with non-zero derivedFromScope, +// if additional L1 data is needed to cross-verify the candidate L2 block. +func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + + xDB, ok := db.crossDBs[chain] + if !ok { + return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain + } + + lDB, ok := db.localDBs[chain] + if !ok { + return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain + } + + // Example: + // A B C D <- L1 + // 1 2 <- L2 + // return: + // (A, 0) -> initial scope, no L2 block yet. Genesis found to be cross-safe + // (A, 1) -> 1 is determined cross-safe, won't be a candidate anymore after. 2 is the new candidate + // (B, 2) -> 2 is out of scope, go to B + // (C, 2) -> 2 is out of scope, go to C + // (D, 2) -> 2 is in scope, stay on D, promote candidate to cross-safe + // (D, 3) -> look at 3 next, see if we have to bump L1 yet, try with same L1 scope first + + crossDerivedFrom, crossDerived, err := xDB.Latest() + if err != nil { + if errors.Is(err, types.ErrFuture) { + // If we do not have any cross-safe block yet, then return the first local-safe block. + derivedFrom, derived, err := lDB.First() + if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find first local-safe block: %w", err) + } + // First block has no parent + return derivedFrom.WithParent(eth.BlockID{}), + derived.WithParent(eth.BlockID{}), nil + } + return eth.BlockRef{}, eth.BlockRef{}, err + } + // Find the local-safe block that comes right after the last seen cross-safe block. + // Just L2 block by block traversal, conditional on being local-safe. + // This will be the candidate L2 block to promote. + + // While the local-safe block isn't cross-safe given limited L1 scope, we'll keep bumping the L1 scope, + // And update cross-safe accordingly. + // This method will keep returning the latest known scope that has been verified to be cross-safe. + candidateFrom, candidate, err := lDB.NextDerived(crossDerived.ID()) + if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, err + } + + candidateRef := candidate.WithParent(crossDerived.ID()) + + parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID()) + if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err) + } + candidateFromRef := candidateFrom.WithParent(parentDerivedFrom.ID()) + + // Allow increment of DA by 1, if we know the floor (due to local safety) is 1 ahead of the current cross-safe L1 scope. + if candidateFrom.Number > crossDerivedFrom.Number+1 { + // If we are not ready to process the candidate block, + // then we need to stick to the current scope, so the caller can bump up from there. + parent, err := lDB.PreviousDerivedFrom(crossDerivedFrom.ID()) + if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", + crossDerivedFrom, err) + } + crossDerivedFromRef := crossDerivedFrom.WithParent(parent.ID()) + return crossDerivedFromRef, eth.BlockRef{}, + fmt.Errorf("candidate is from %s, while current scope is %s: %w", + candidateFrom, crossDerivedFrom, types.ErrOutOfScope) + } + return candidateFromRef, candidateRef, nil +} + +func (db *ChainsDB) PreviousDerived(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + lDB, ok := db.localDBs[chain] + if !ok { + return types.BlockSeal{}, types.ErrUnknownChain + } + return lDB.PreviousDerived(derived) +} + +func (db *ChainsDB) PreviousDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (prevDerivedFrom types.BlockSeal, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + lDB, ok := db.localDBs[chain] + if !ok { + return types.BlockSeal{}, types.ErrUnknownChain + } + return lDB.PreviousDerivedFrom(derivedFrom) +} + +func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) { + db.mu.RLock() + defer db.mu.RUnlock() + lDB, ok := db.localDBs[chain] + if !ok { + return eth.BlockRef{}, types.ErrUnknownChain + } + v, err := lDB.NextDerivedFrom(derivedFrom) + if err != nil { + return eth.BlockRef{}, err + } + return v.WithParent(derivedFrom), nil +} + // Safest returns the strongest safety level that can be guaranteed for the given log entry. // it assumes the log entry has already been checked and is valid, this function only checks safety levels. // Cross-safety levels are all considered to be more safe than any form of local-safety. @@ -181,7 +389,7 @@ func (db *ChainsDB) Safest(chainID types.ChainID, blockNum uint64, index uint32) func (db *ChainsDB) IteratorStartingAt(chain types.ChainID, sealedNum uint64, logIndex uint32) (logs.Iterator, error) { logDB, ok := db.logDBs[chain] if !ok { - return nil, fmt.Errorf("%w: %v", ErrUnknownChain, chain) + return nil, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } return logDB.IteratorStartingAt(sealedNum, logIndex) } diff --git a/op-supervisor/supervisor/backend/db/update.go b/op-supervisor/supervisor/backend/db/update.go index 9825b76fdafc..e45fd3f469a5 100644 --- a/op-supervisor/supervisor/backend/db/update.go +++ b/op-supervisor/supervisor/backend/db/update.go @@ -20,7 +20,7 @@ func (db *ChainsDB) AddLog( logDB, ok := db.logDBs[chain] if !ok { - return fmt.Errorf("cannot AddLog: %w: %v", ErrUnknownChain, chain) + return fmt.Errorf("cannot AddLog: %w: %v", types.ErrUnknownChain, chain) } return logDB.AddLog(logHash, parentBlock, logIdx, execMsg) } @@ -31,8 +31,9 @@ func (db *ChainsDB) SealBlock(chain types.ChainID, block eth.BlockRef) error { logDB, ok := db.logDBs[chain] if !ok { - return fmt.Errorf("cannot SealBlock: %w: %v", ErrUnknownChain, chain) + return fmt.Errorf("cannot SealBlock: %w: %v", types.ErrUnknownChain, chain) } + db.logger.Debug("Updating local unsafe", "chain", chain, "block", block) err := logDB.SealBlock(block.ParentHash, block.ID(), block.Time) if err != nil { return fmt.Errorf("failed to seal block %v: %w", block, err) @@ -46,7 +47,7 @@ func (db *ChainsDB) Rewind(chain types.ChainID, headBlockNum uint64) error { logDB, ok := db.logDBs[chain] if !ok { - return fmt.Errorf("cannot Rewind: %w: %s", ErrUnknownChain, chain) + return fmt.Errorf("cannot Rewind: %w: %s", types.ErrUnknownChain, chain) } return logDB.Rewind(headBlockNum) } @@ -57,8 +58,9 @@ func (db *ChainsDB) UpdateLocalSafe(chain types.ChainID, derivedFrom eth.BlockRe localDB, ok := db.localDBs[chain] if !ok { - return fmt.Errorf("cannot UpdateLocalSafe: %w: %v", ErrUnknownChain, chain) + return fmt.Errorf("cannot UpdateLocalSafe: %w: %v", types.ErrUnknownChain, chain) } + db.logger.Debug("Updating local safe", "chain", chain, "derivedFrom", derivedFrom, "lastDerived", lastDerived) return localDB.AddDerived(derivedFrom, lastDerived) } @@ -67,8 +69,9 @@ func (db *ChainsDB) UpdateCrossUnsafe(chain types.ChainID, crossUnsafe types.Blo defer db.mu.RUnlock() if _, ok := db.crossUnsafe[chain]; !ok { - return fmt.Errorf("cannot UpdateCrossUnsafe: %w: %s", ErrUnknownChain, chain) + return fmt.Errorf("cannot UpdateCrossUnsafe: %w: %s", types.ErrUnknownChain, chain) } + db.logger.Debug("Updating cross unsafe", "chain", chain, "crossUnsafe", crossUnsafe) db.crossUnsafe[chain] = crossUnsafe return nil } @@ -79,8 +82,9 @@ func (db *ChainsDB) UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, la crossDB, ok := db.crossDBs[chain] if !ok { - return fmt.Errorf("cannot UpdateCrossSafe: %w: %s", ErrUnknownChain, chain) + return fmt.Errorf("cannot UpdateCrossSafe: %w: %s", types.ErrUnknownChain, chain) } + db.logger.Debug("Updating cross safe", "chain", chain, "l1View", l1View, "lastCrossDerived", lastCrossDerived) return crossDB.AddDerived(l1View, lastCrossDerived) } @@ -91,6 +95,7 @@ func (db *ChainsDB) UpdateFinalizedL1(finalized eth.BlockRef) error { if db.finalizedL1.Number > finalized.Number { return fmt.Errorf("cannot rewind finalized L1 head from %s to %s", db.finalizedL1, finalized) } + db.logger.Debug("Updating finalized L1", "finalizedL1", finalized) db.finalizedL1 = finalized return nil } diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go index 213b6ce1bad9..07c05ac6dc67 100644 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go +++ b/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go @@ -76,7 +76,7 @@ func (i *CrossL2Inbox) DecodeExecutingMessageLog(l *ethTypes.Log) (types.Executi } hash := payloadHashToLogHash(msgHash, identifier.Origin) return types.ExecutingMessage{ - Chain: chainID, + Chain: types.ChainIndex(chainID), // TODO(#11105): translate chain ID to chain index Hash: hash, BlockNum: identifier.BlockNumber.Uint64(), LogIdx: uint32(identifier.LogIndex.Uint64()), diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go index 302b188e5cdf..de5fe0eb2706 100644 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go +++ b/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go @@ -20,14 +20,14 @@ func TestDecodeExecutingMessageEvent(t *testing.T) { payload := bytes.Repeat([]byte{0xaa, 0xbb}, 50) payloadHash := crypto.Keccak256Hash(payload) expected := types.ExecutingMessage{ - Chain: 42424, + Chain: 42424, // TODO(#11105): translate chain ID to chain index BlockNum: 12345, LogIdx: 98, Timestamp: 9578295, } contractIdent := contractIdentifier{ Origin: common.Address{0xbb, 0xcc}, - ChainId: new(big.Int).SetUint64(uint64(expected.Chain)), + ChainId: new(big.Int).SetUint64(uint64(expected.Chain)), // TODO(#11105): translate chain ID to chain index BlockNumber: new(big.Int).SetUint64(expected.BlockNum), Timestamp: new(big.Int).SetUint64(expected.Timestamp), LogIndex: new(big.Int).SetUint64(uint64(expected.LogIdx)), diff --git a/op-supervisor/supervisor/backend/db/entrydb/error.go b/op-supervisor/supervisor/types/error.go similarity index 63% rename from op-supervisor/supervisor/backend/db/entrydb/error.go rename to op-supervisor/supervisor/types/error.go index 42ae26dcf112..b4e1c57ba908 100644 --- a/op-supervisor/supervisor/backend/db/entrydb/error.go +++ b/op-supervisor/supervisor/types/error.go @@ -1,4 +1,4 @@ -package entrydb +package types import "errors" @@ -17,4 +17,11 @@ var ( ErrConflict = errors.New("conflicting data") // ErrStop can be used in iterators to indicate iteration has to stop ErrStop = errors.New("iter stop") + // ErrOutOfScope is when data is accessed, but access is not allowed, because of a limited scope. + // E.g. when limiting scope to L2 blocks derived from a specific subset of the L1 chain. + ErrOutOfScope = errors.New("out of scope") + // ErrUnknownChain is when a chain is unknown, not in the dependency set. + ErrUnknownChain = errors.New("unknown chain") + // ErrNoRPCSource happens when a sub-service needs an RPC data source, but is not configured with one. + ErrNoRPCSource = errors.New("no RPC client configured") ) diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index 05a9b1633126..1c97a9119ce6 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "strconv" "github.com/holiman/uint256" @@ -15,8 +16,28 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" ) +// ChainIndex represents the lifetime of a chain in a dependency set. +type ChainIndex uint32 + +func (ci ChainIndex) String() string { + return strconv.FormatUint(uint64(ci), 10) +} + +func (ci ChainIndex) MarshalText() ([]byte, error) { + return []byte(ci.String()), nil +} + +func (ci *ChainIndex) UnmarshalText(data []byte) error { + v, err := strconv.ParseUint(string(data), 10, 32) + if err != nil { + return err + } + *ci = ChainIndex(v) + return nil +} + type ExecutingMessage struct { - Chain uint32 // same as ChainID for now, but will be indirect, i.e. translated to full ID, later + Chain ChainIndex // same as ChainID for now, but will be indirect, i.e. translated to full ID, later BlockNum uint64 LogIdx uint32 Timestamp uint64 @@ -24,7 +45,7 @@ type ExecutingMessage struct { } func (s *ExecutingMessage) String() string { - return fmt.Sprintf("ExecMsg(chain: %d, block: %d, log: %d, time: %d, logHash: %s)", + return fmt.Sprintf("ExecMsg(chainIndex: %s, block: %d, log: %d, time: %d, logHash: %s)", s.Chain, s.BlockNum, s.LogIdx, s.Timestamp, s.Hash) } @@ -208,3 +229,25 @@ func (s BlockSeal) String() string { func (s BlockSeal) ID() eth.BlockID { return eth.BlockID{Hash: s.Hash, Number: s.Number} } + +func (s BlockSeal) WithParent(parent eth.BlockID) eth.BlockRef { + // prevent parent attachment if the parent is not the previous block, + // and the block is not the genesis block + if s.Number != parent.Number+1 && s.Number != 0 { + panic(fmt.Errorf("invalid parent block %s to combine with %s", parent, s)) + } + return eth.BlockRef{ + Hash: s.Hash, + Number: s.Number, + ParentHash: parent.Hash, + Time: s.Timestamp, + } +} + +func BlockSealFromRef(ref eth.BlockRef) BlockSeal { + return BlockSeal{ + Hash: ref.Hash, + Number: ref.Number, + Timestamp: ref.Time, + } +} From 8772224726eb280cc2bea9844c51d08c58e384cc Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 24 Oct 2024 18:38:41 +0200 Subject: [PATCH 022/451] Op-supervisor: dependency set improvements (#12623) * op-supervisor: DB improvements for cross-safe updates Co-authored-by: axelKingsley Co-authored-by: Tyler Smith * op-supervisor: dependency-set improvements Co-authored-by: axelKingsley Co-authored-by: Tyler Smith --------- Co-authored-by: axelKingsley Co-authored-by: Tyler Smith --- op-e2e/interop/supersystem.go | 15 ++-- op-supervisor/config/config_test.go | 14 +-- op-supervisor/supervisor/backend/backend.go | 7 ++ .../supervisor/backend/backend_test.go | 37 ++++---- .../supervisor/backend/depset/depset.go | 6 ++ .../supervisor/backend/depset/depset_test.go | 10 ++- .../supervisor/backend/depset/static.go | 89 ++++++++++++++++--- op-supervisor/supervisor/service_test.go | 9 +- 8 files changed, 141 insertions(+), 46 deletions(-) diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index b973a30d50cc..7e9e20519567 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -438,18 +438,23 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService { L2RPCs: []string{}, Datadir: path.Join(s.t.TempDir(), "supervisor"), } - depSet := &depset.StaticConfigDependencySet{ - Dependencies: make(map[supervisortypes.ChainID]*depset.StaticConfigDependency), - } + depSet := make(map[supervisortypes.ChainID]*depset.StaticConfigDependency) + // Iterate over the L2 chain configs. The L2 nodes don't exist yet. for _, l2Out := range s.worldOutput.L2s { chainID := supervisortypes.ChainIDFromBig(l2Out.Genesis.Config.ChainID) - depSet.Dependencies[chainID] = &depset.StaticConfigDependency{ + index, err := chainID.ToUInt32() + require.NoError(s.t, err) + depSet[chainID] = &depset.StaticConfigDependency{ + ChainIndex: supervisortypes.ChainIndex(index), ActivationTime: 0, HistoryMinTime: 0, } } - cfg.DependencySetSource = depSet + stDepSet, err := depset.NewStaticConfigDependencySet(depSet) + require.NoError(s.t, err) + cfg.DependencySetSource = stDepSet + // Create the supervisor with the configuration super, err := supervisor.SupervisorFromConfig(context.Background(), cfg, logger) require.NoError(s.t, err) diff --git a/op-supervisor/config/config_test.go b/op-supervisor/config/config_test.go index fd57c7b95d97..0d354ca134a5 100644 --- a/op-supervisor/config/config_test.go +++ b/op-supervisor/config/config_test.go @@ -56,13 +56,15 @@ func TestValidateRPCConfig(t *testing.T) { } func validConfig() *Config { - depSet := &depset.StaticConfigDependencySet{ - Dependencies: map[types.ChainID]*depset.StaticConfigDependency{ - types.ChainIDFromUInt64(900): &depset.StaticConfigDependency{ - ActivationTime: 0, - HistoryMinTime: 0, - }, + depSet, err := depset.NewStaticConfigDependencySet(map[types.ChainID]*depset.StaticConfigDependency{ + types.ChainIDFromUInt64(900): &depset.StaticConfigDependency{ + ChainIndex: 900, + ActivationTime: 0, + HistoryMinTime: 0, }, + }) + if err != nil { + panic(err) } // Should be valid using only the required arguments passed in via the constructor. return NewConfig([]string{"http://localhost:8545"}, depSet, "./supervisor_testdir") diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 27ed1ec3e984..506bcefd67c0 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -261,6 +261,13 @@ func (su *SupervisorBackend) AddL2RPC(ctx context.Context, rpc string) error { return su.attachRPC(ctx, rpc) } +// Internal methods, for processors +// ---------------------------- + +func (su *SupervisorBackend) DependencySet() depset.DependencySet { + return su.depSet +} + // Query methods // ---------------------------- diff --git a/op-supervisor/supervisor/backend/backend_test.go b/op-supervisor/supervisor/backend/backend_test.go index 80c773a5ea46..3d2b5ad123a5 100644 --- a/op-supervisor/supervisor/backend/backend_test.go +++ b/op-supervisor/supervisor/backend/backend_test.go @@ -31,24 +31,27 @@ func TestBackendLifetime(t *testing.T) { dataDir := t.TempDir() chainA := types.ChainIDFromUInt64(900) chainB := types.ChainIDFromUInt64(901) - cfg := &config.Config{ - Version: "test", - LogConfig: oplog.CLIConfig{}, - MetricsConfig: opmetrics.CLIConfig{}, - PprofConfig: oppprof.CLIConfig{}, - RPC: oprpc.CLIConfig{}, - DependencySetSource: &depset.StaticConfigDependencySet{ - Dependencies: map[types.ChainID]*depset.StaticConfigDependency{ - chainA: { - ActivationTime: 42, - HistoryMinTime: 100, - }, - chainB: { - ActivationTime: 30, - HistoryMinTime: 20, - }, + depSet, err := depset.NewStaticConfigDependencySet( + map[types.ChainID]*depset.StaticConfigDependency{ + chainA: { + ChainIndex: 900, + ActivationTime: 42, + HistoryMinTime: 100, + }, + chainB: { + ChainIndex: 901, + ActivationTime: 30, + HistoryMinTime: 20, }, - }, + }) + require.NoError(t, err) + cfg := &config.Config{ + Version: "test", + LogConfig: oplog.CLIConfig{}, + MetricsConfig: opmetrics.CLIConfig{}, + PprofConfig: oppprof.CLIConfig{}, + RPC: oprpc.CLIConfig{}, + DependencySetSource: depSet, SynchronousProcessors: true, MockRun: false, L2RPCs: nil, diff --git a/op-supervisor/supervisor/backend/depset/depset.go b/op-supervisor/supervisor/backend/depset/depset.go index 06127726f000..da32b7719008 100644 --- a/op-supervisor/supervisor/backend/depset/depset.go +++ b/op-supervisor/supervisor/backend/depset/depset.go @@ -32,4 +32,10 @@ type DependencySet interface { // HasChain determines if a chain is being tracked for interop purposes. // See CanExecuteAt and CanInitiateAt to check if a chain may message at a given time. HasChain(chainID types.ChainID) bool + + // ChainIndexFromID converts a ChainID to a ChainIndex. + ChainIndexFromID(id types.ChainID) (types.ChainIndex, error) + + // ChainIDFromIndex converts a ChainIndex to a ChainID. + ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error) } diff --git a/op-supervisor/supervisor/backend/depset/depset_test.go b/op-supervisor/supervisor/backend/depset/depset_test.go index 42dbb284d6e2..8b71b38ef46e 100644 --- a/op-supervisor/supervisor/backend/depset/depset_test.go +++ b/op-supervisor/supervisor/backend/depset/depset_test.go @@ -15,18 +15,20 @@ import ( func TestDependencySet(t *testing.T) { d := path.Join(t.TempDir(), "tmp_dep_set.json") - depSet := &StaticConfigDependencySet{ - Dependencies: map[types.ChainID]*StaticConfigDependency{ + depSet, err := NewStaticConfigDependencySet( + map[types.ChainID]*StaticConfigDependency{ types.ChainIDFromUInt64(900): { + ChainIndex: 900, ActivationTime: 42, HistoryMinTime: 100, }, types.ChainIDFromUInt64(901): { + ChainIndex: 901, ActivationTime: 30, HistoryMinTime: 20, }, - }, - } + }) + require.NoError(t, err) data, err := json.Marshal(depSet) require.NoError(t, err) diff --git a/op-supervisor/supervisor/backend/depset/static.go b/op-supervisor/supervisor/backend/depset/static.go index 0ef2874bb527..f7cf1c166de5 100644 --- a/op-supervisor/supervisor/backend/depset/static.go +++ b/op-supervisor/supervisor/backend/depset/static.go @@ -2,14 +2,18 @@ package depset import ( "context" + "encoding/json" + "fmt" + "slices" "sort" - "golang.org/x/exp/maps" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) type StaticConfigDependency struct { + // ChainIndex is the unique short identifier of this chain. + ChainIndex types.ChainIndex `json:"chainIndex"` + // ActivationTime is when the chain becomes part of the dependency set. // This is the minimum timestamp of the inclusion of an executing message. ActivationTime uint64 `json:"activationTime"` @@ -23,9 +27,62 @@ type StaticConfigDependency struct { // StaticConfigDependencySet statically declares a DependencySet. // It can be used as a DependencySetSource itself, by simply returning the itself when loading the set. type StaticConfigDependencySet struct { + // dependency info per chain + dependencies map[types.ChainID]*StaticConfigDependency + // cached mapping of chain index to chain ID + indexToID map[types.ChainIndex]types.ChainID + // cached list of chain IDs, sorted by ID value + chainIDs []types.ChainID +} + +func NewStaticConfigDependencySet(dependencies map[types.ChainID]*StaticConfigDependency) (*StaticConfigDependencySet, error) { + out := &StaticConfigDependencySet{dependencies: dependencies} + if err := out.hydrate(); err != nil { + return nil, err + } + return out, nil +} + +// jsonStaticConfigDependencySet is a util for JSON encoding/decoding, +// to encode/decode just the attributes that matter, +// while wrapping the decoding functionality with additional hydration step. +type jsonStaticConfigDependencySet struct { Dependencies map[types.ChainID]*StaticConfigDependency `json:"dependencies"` } +func (ds *StaticConfigDependencySet) MarshalJSON() ([]byte, error) { + out := &jsonStaticConfigDependencySet{ + Dependencies: ds.dependencies, + } + return json.Marshal(out) +} + +func (ds *StaticConfigDependencySet) UnmarshalJSON(data []byte) error { + var v jsonStaticConfigDependencySet + if err := json.Unmarshal(data, &v); err != nil { + return err + } + ds.dependencies = v.Dependencies + return ds.hydrate() +} + +// hydrate sets all the cached values, based on the dependencies attribute +func (ds *StaticConfigDependencySet) hydrate() error { + ds.indexToID = make(map[types.ChainIndex]types.ChainID) + ds.chainIDs = make([]types.ChainID, 0, len(ds.dependencies)) + for id, dep := range ds.dependencies { + if existing, ok := ds.indexToID[dep.ChainIndex]; ok { + return fmt.Errorf("chain %s cannot have the same index (%d) as chain %s", id, dep.ChainIndex, existing) + } + ds.indexToID[dep.ChainIndex] = id + ds.chainIDs = append(ds.chainIDs, id) + } + sort.Slice(ds.chainIDs, func(i, j int) bool { + return ds.chainIDs[i].Cmp(ds.chainIDs[j]) < 0 + }) + return nil +} + var _ DependencySetSource = (*StaticConfigDependencySet)(nil) var _ DependencySet = (*StaticConfigDependencySet)(nil) @@ -35,7 +92,7 @@ func (ds *StaticConfigDependencySet) LoadDependencySet(ctx context.Context) (Dep } func (ds *StaticConfigDependencySet) CanExecuteAt(chainID types.ChainID, execTimestamp uint64) (bool, error) { - dep, ok := ds.Dependencies[chainID] + dep, ok := ds.dependencies[chainID] if !ok { return false, nil } @@ -43,7 +100,7 @@ func (ds *StaticConfigDependencySet) CanExecuteAt(chainID types.ChainID, execTim } func (ds *StaticConfigDependencySet) CanInitiateAt(chainID types.ChainID, initTimestamp uint64) (bool, error) { - dep, ok := ds.Dependencies[chainID] + dep, ok := ds.dependencies[chainID] if !ok { return false, nil } @@ -51,14 +108,26 @@ func (ds *StaticConfigDependencySet) CanInitiateAt(chainID types.ChainID, initTi } func (ds *StaticConfigDependencySet) Chains() []types.ChainID { - out := maps.Keys(ds.Dependencies) - sort.Slice(out, func(i, j int) bool { - return out[i].Cmp(out[j]) < 0 - }) - return out + return slices.Clone(ds.chainIDs) } func (ds *StaticConfigDependencySet) HasChain(chainID types.ChainID) bool { - _, ok := ds.Dependencies[chainID] + _, ok := ds.dependencies[chainID] return ok } + +func (ds *StaticConfigDependencySet) ChainIndexFromID(id types.ChainID) (types.ChainIndex, error) { + dep, ok := ds.dependencies[id] + if !ok { + return 0, types.ErrUnknownChain + } + return dep.ChainIndex, nil +} + +func (ds *StaticConfigDependencySet) ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error) { + id, ok := ds.indexToID[index] + if !ok { + return types.ChainID{}, types.ErrUnknownChain + } + return id, nil +} diff --git a/op-supervisor/supervisor/service_test.go b/op-supervisor/supervisor/service_test.go index fad118e96a82..9fce3ec48b4c 100644 --- a/op-supervisor/supervisor/service_test.go +++ b/op-supervisor/supervisor/service_test.go @@ -22,6 +22,9 @@ import ( ) func TestSupervisorService(t *testing.T) { + depSet, err := depset.NewStaticConfigDependencySet(make(map[types.ChainID]*depset.StaticConfigDependency)) + require.NoError(t, err) + cfg := &config.Config{ Version: "", LogConfig: oplog.CLIConfig{ @@ -47,10 +50,8 @@ func TestSupervisorService(t *testing.T) { ListenPort: 0, // pick a port automatically EnableAdmin: true, }, - DependencySetSource: &depset.StaticConfigDependencySet{ - Dependencies: make(map[types.ChainID]*depset.StaticConfigDependency), - }, - MockRun: true, + DependencySetSource: depSet, + MockRun: true, } logger := testlog.Logger(t, log.LevelError) supervisor, err := SupervisorFromConfig(context.Background(), cfg, logger) From bf28a04e72a4c168262cef4c638c3c9eacaa32e6 Mon Sep 17 00:00:00 2001 From: clabby Date: Thu, 24 Oct 2024 15:47:03 -0400 Subject: [PATCH 023/451] chore(op-e2e): Run `BadTxInBatch` tests on Granite only (#12629) --- op-e2e/actions/proofs/bad_tx_in_batch_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/op-e2e/actions/proofs/bad_tx_in_batch_test.go b/op-e2e/actions/proofs/bad_tx_in_batch_test.go index f67e216f76cb..6aed725f2be8 100644 --- a/op-e2e/actions/proofs/bad_tx_in_batch_test.go +++ b/op-e2e/actions/proofs/bad_tx_in_batch_test.go @@ -144,14 +144,14 @@ func Test_ProgramAction_BadTxInBatch(gt *testing.T) { matrix.AddTestCase( "HonestClaim", nil, - helpers.LatestForkOnly, + helpers.NewForkMatrix(helpers.Granite), runBadTxInBatchTest, helpers.ExpectNoError(), ) matrix.AddTestCase( "JunkClaim", nil, - helpers.LatestForkOnly, + helpers.NewForkMatrix(helpers.Granite), runBadTxInBatchTest, helpers.ExpectError(claim.ErrClaimNotValid), helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), @@ -159,14 +159,14 @@ func Test_ProgramAction_BadTxInBatch(gt *testing.T) { matrix.AddTestCase( "ResubmitBadFirstFrame-HonestClaim", nil, - helpers.LatestForkOnly, + helpers.NewForkMatrix(helpers.Granite), runBadTxInBatch_ResubmitBadFirstFrame_Test, helpers.ExpectNoError(), ) matrix.AddTestCase( "ResubmitBadFirstFrame-JunkClaim", nil, - helpers.LatestForkOnly, + helpers.NewForkMatrix(helpers.Granite), runBadTxInBatch_ResubmitBadFirstFrame_Test, helpers.ExpectError(claim.ErrClaimNotValid), helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), From 0f9897b0686f8b883cdf0d232c6696447a5c185c Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 24 Oct 2024 13:54:34 -0600 Subject: [PATCH 024/451] op-e2e: Improve WaitForBlock timeouts (#12627) Update WaitForBlock to maintain two timeouts: a no-change timeout, which fires if the chain's head does not change within a specified window, and an absolute timeout, which fires if the chain's head does not meet or exceed the specified block. These changes should ideally reduce the number of test flakes we're seeing. Everything takes longer when test executors are under load; by maintaining these two timeouts we can provide longer-running tests with more buffer while retaining the ability to fail fast if the chain gets stuck. As part of this PR I also refactored the wait method to use polling rather than WebSockets. I've found WebSockets to be unreliable in tests. --- op-e2e/e2eutils/disputegame/helper.go | 2 +- op-e2e/e2eutils/geth/wait.go | 88 +++++++++++++++---- op-e2e/system/altda/concurrent_test.go | 4 +- op-e2e/system/da/multi_test.go | 4 +- op-e2e/system/da/startstop_test.go | 4 +- op-e2e/system/e2esys/setup.go | 2 +- op-e2e/system/fees/eip1559params_test.go | 2 +- op-e2e/system/fees/fees_test.go | 3 +- op-e2e/system/fees/l1info_test.go | 4 +- op-e2e/system/p2p/txpool_test.go | 3 +- op-e2e/system/proofs/proposer_fp_test.go | 2 +- op-e2e/system/proofs/proposer_l2oo_test.go | 2 +- .../system/verifier/sequencer_window_test.go | 2 +- 13 files changed, 89 insertions(+), 33 deletions(-) diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go index 07ee4c659ff9..658eedffc514 100644 --- a/op-e2e/e2eutils/disputegame/helper.go +++ b/op-e2e/e2eutils/disputegame/helper.go @@ -291,7 +291,7 @@ func (h *FactoryHelper) WaitForBlock(l2Node string, l2BlockNumber uint64, cfg *G l2Client := h.System.NodeClient(l2Node) if cfg.allowUnsafe { - _, err := geth.WaitForBlock(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute) + _, err := geth.WaitForBlock(new(big.Int).SetUint64(l2BlockNumber), l2Client, geth.WithAbsoluteTimeout(time.Minute)) h.Require.NoErrorf(err, "Block number %v did not become unsafe", l2BlockNumber) } else { _, err := geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute) diff --git a/op-e2e/e2eutils/geth/wait.go b/op-e2e/e2eutils/geth/wait.go index 267e589f021c..8356058afda7 100644 --- a/op-e2e/e2eutils/geth/wait.go +++ b/op-e2e/e2eutils/geth/wait.go @@ -17,7 +17,10 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -const errStrTxIdxingInProgress = "transaction indexing is in progress" +const ( + errStrTxIdxingInProgress = "transaction indexing is in progress" + waitForBlockMaxRetries = 3 +) // errTimeout represents a timeout var errTimeout = errors.New("timeout") @@ -83,25 +86,80 @@ func WaitForTransaction(hash common.Hash, client *ethclient.Client, timeout time } } -func WaitForBlock(number *big.Int, client *ethclient.Client, timeout time.Duration) (*types.Block, error) { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() +type waitForBlockOptions struct { + noChangeTimeout time.Duration + absoluteTimeout time.Duration +} - headChan := make(chan *types.Header, 100) - headSub, err := client.SubscribeNewHead(ctx, headChan) - if err != nil { - return nil, err +func WithNoChangeTimeout(timeout time.Duration) WaitForBlockOption { + return func(o *waitForBlockOptions) { + o.noChangeTimeout = timeout } - defer headSub.Unsubscribe() +} + +func WithAbsoluteTimeout(timeout time.Duration) WaitForBlockOption { + return func(o *waitForBlockOptions) { + o.absoluteTimeout = timeout + } +} + +type WaitForBlockOption func(*waitForBlockOptions) + +// WaitForBlock waits for the chain to advance to the provided block number. It can be configured with +// two different timeout: an absolute timeout, and a no change timeout. The absolute timeout caps +// the maximum amount of time this method will run. The no change timeout will return an error if the +// block number does not change within that time window. This is useful to bail out early in the event +// of a stuck chain, but allow things to continue if the chain is still advancing. +// +// This function will also retry fetch errors up to three times before returning an error in order to +// protect against transient network problems. This function uses polling rather than websockets. +func WaitForBlock(number *big.Int, client *ethclient.Client, opts ...WaitForBlockOption) (*types.Block, error) { + defaultOpts := &waitForBlockOptions{ + noChangeTimeout: 30 * time.Second, + absoluteTimeout: 3 * time.Minute, + } + for _, opt := range opts { + opt(defaultOpts) + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultOpts.absoluteTimeout) + defer cancel() + + lastAdvancement := time.Now() + lastBlockNumber := big.NewInt(0) + + pollTicker := time.NewTicker(500 * time.Millisecond) + defer pollTicker.Stop() + var errCount int for { - select { - case head := <-headChan: - if head.Number.Cmp(number) >= 0 { - return client.BlockByNumber(ctx, number) + head, err := client.BlockByNumber(ctx, nil) + if err != nil { + errCount++ + if errCount >= waitForBlockMaxRetries { + return nil, fmt.Errorf("head fetching exceeded max retries. last error: %w", err) } - case err := <-headSub.Err(): - return nil, fmt.Errorf("error in head subscription: %w", err) + continue + } + + errCount = 0 + + if head.Number().Cmp(number) >= 0 { + return client.BlockByNumber(ctx, number) + } + + if head.Number().Cmp(lastBlockNumber) != 0 { + lastBlockNumber = head.Number() + lastAdvancement = time.Now() + } + + if time.Since(lastAdvancement) > defaultOpts.noChangeTimeout { + return nil, fmt.Errorf("block number %d has not changed in %s", lastBlockNumber, defaultOpts.noChangeTimeout) + } + + select { + case <-pollTicker.C: + continue case <-ctx.Done(): return nil, ctx.Err() } diff --git a/op-e2e/system/altda/concurrent_test.go b/op-e2e/system/altda/concurrent_test.go index a2ad1f8bcbb8..32506e5c4a10 100644 --- a/op-e2e/system/altda/concurrent_test.go +++ b/op-e2e/system/altda/concurrent_test.go @@ -43,7 +43,7 @@ func TestBatcherConcurrentAltDARequests(t *testing.T) { l2Seq := sys.NodeClient("sequencer") // we wait for numL1TxsExpected L2 blocks to have been produced, just to make sure the sequencer is working properly - _, err = geth.WaitForBlock(big.NewInt(numL1TxsExpected), l2Seq, time.Duration(cfg.DeployConfig.L2BlockTime*uint64(numL1TxsExpected))*time.Second) + _, err = geth.WaitForBlock(big.NewInt(numL1TxsExpected), l2Seq) require.NoError(t, err, "Waiting for L2 blocks") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -59,7 +59,7 @@ func TestBatcherConcurrentAltDARequests(t *testing.T) { // exceed the number of blocks. checkBlocks := 10 for i := 0; i < checkBlocks; i++ { - block, err := geth.WaitForBlock(big.NewInt(int64(startingL1BlockNum)+int64(i)), l1Client, time.Duration(cfg.DeployConfig.L1BlockTime*2)*time.Second) + block, err := geth.WaitForBlock(big.NewInt(int64(startingL1BlockNum)+int64(i)), l1Client) require.NoError(t, err, "Waiting for l1 blocks") // there are possibly other services (proposer/challenger) in the background sending txs // so we only count the batcher txs diff --git a/op-e2e/system/da/multi_test.go b/op-e2e/system/da/multi_test.go index 8272930da765..3d150010a0f3 100644 --- a/op-e2e/system/da/multi_test.go +++ b/op-e2e/system/da/multi_test.go @@ -29,7 +29,7 @@ func TestBatcherMultiTx(t *testing.T) { l1Client := sys.NodeClient("l1") l2Seq := sys.NodeClient("sequencer") - _, err = geth.WaitForBlock(big.NewInt(10), l2Seq, time.Duration(cfg.DeployConfig.L2BlockTime*15)*time.Second) + _, err = geth.WaitForBlock(big.NewInt(10), l2Seq) require.NoError(t, err, "Waiting for L2 blocks") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -47,7 +47,7 @@ func TestBatcherMultiTx(t *testing.T) { // possible additional L1 blocks will be created before the batcher starts, // so we wait additional blocks. for i := int64(0); i < 5; i++ { - block, err := geth.WaitForBlock(big.NewInt(int64(l1Number)+i), l1Client, time.Duration(cfg.DeployConfig.L1BlockTime*2)*time.Second) + block, err := geth.WaitForBlock(big.NewInt(int64(l1Number)+i), l1Client) require.NoError(t, err, "Waiting for l1 blocks") // there are possibly other services (proposer/challenger) in the background sending txs // so we only count the batcher txs diff --git a/op-e2e/system/da/startstop_test.go b/op-e2e/system/da/startstop_test.go index e085029983af..2a5e0826f06e 100644 --- a/op-e2e/system/da/startstop_test.go +++ b/op-e2e/system/da/startstop_test.go @@ -72,7 +72,7 @@ func testStartStopBatcher(t *testing.T, cfgMod func(*e2esys.SystemConfig)) { // wait until the block the tx was first included in shows up in the safe chain on the verifier safeBlockInclusionDuration := time.Duration(6*cfg.DeployConfig.L1BlockTime) * time.Second - _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif, safeBlockInclusionDuration) + _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif) require.NoError(t, err, "Waiting for block on verifier") require.NoError(t, wait.ForProcessingFullBatch(context.Background(), rollupClient)) @@ -111,7 +111,7 @@ func testStartStopBatcher(t *testing.T, cfgMod func(*e2esys.SystemConfig)) { receipt = sendTx() // wait until the block the tx was first included in shows up in the safe chain on the verifier - _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif, safeBlockInclusionDuration) + _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif) require.NoError(t, err, "Waiting for block on verifier") require.NoError(t, wait.ForProcessingFullBatch(context.Background(), rollupClient)) diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index fc557aa1a932..5d046b3c649c 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -690,7 +690,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, } l1Client := sys.NodeClient(RoleL1) - _, err = geth.WaitForBlock(big.NewInt(2), l1Client, 6*time.Second*time.Duration(cfg.DeployConfig.L1BlockTime)) + _, err = geth.WaitForBlock(big.NewInt(2), l1Client) if err != nil { return nil, fmt.Errorf("waiting for blocks: %w", err) } diff --git a/op-e2e/system/fees/eip1559params_test.go b/op-e2e/system/fees/eip1559params_test.go index b9f5d66f02fb..e1b1098cac12 100644 --- a/op-e2e/system/fees/eip1559params_test.go +++ b/op-e2e/system/fees/eip1559params_test.go @@ -95,7 +95,7 @@ func TestEIP1559Params(t *testing.T) { delta := ((gasTarget - int64(h.GasUsed)) * h.BaseFee.Int64() / gasTarget / int64(expectedDenom)) expectedNextFee := h.BaseFee.Int64() - delta - b, err := geth.WaitForBlock(big.NewInt(h.Number.Int64()+1), l2Seq, txTimeoutDuration) + b, err := geth.WaitForBlock(big.NewInt(h.Number.Int64()+1), l2Seq) require.NoError(t, err, "waiting for next L2 block") require.Equal(t, expectedNextFee, b.Header().BaseFee.Int64()) diff --git a/op-e2e/system/fees/fees_test.go b/op-e2e/system/fees/fees_test.go index 61590313cbc4..6296b0ee7ab4 100644 --- a/op-e2e/system/fees/fees_test.go +++ b/op-e2e/system/fees/fees_test.go @@ -4,7 +4,6 @@ import ( "context" "math/big" "testing" - "time" op_e2e "github.com/ethereum-optimism/optimism/op-e2e" @@ -90,7 +89,7 @@ func testFees(t *testing.T, cfg e2esys.SystemConfig) { l1 := sys.NodeClient("l1") // Wait for first block after genesis. The genesis block has zero L1Block values and will throw off the GPO checks - _, err = geth.WaitForBlock(big.NewInt(1), l2Verif, time.Minute) + _, err = geth.WaitForBlock(big.NewInt(1), l2Verif) require.NoError(t, err) config := sys.L2Genesis().Config diff --git a/op-e2e/system/fees/l1info_test.go b/op-e2e/system/fees/l1info_test.go index 2fdd3f70747a..a4fc16d94a59 100644 --- a/op-e2e/system/fees/l1info_test.go +++ b/op-e2e/system/fees/l1info_test.go @@ -113,9 +113,9 @@ func TestL1InfoContract(t *testing.T) { endVerifBlockNumber := big.NewInt(4) endSeqBlockNumber := big.NewInt(6) - endVerifBlock, err := geth.WaitForBlock(endVerifBlockNumber, l2Verif, time.Minute) + endVerifBlock, err := geth.WaitForBlock(endVerifBlockNumber, l2Verif) require.Nil(t, err) - endSeqBlock, err := geth.WaitForBlock(endSeqBlockNumber, l2Seq, time.Minute) + endSeqBlock, err := geth.WaitForBlock(endSeqBlockNumber, l2Seq) require.Nil(t, err) seqL1Info, err := bindings.NewL1Block(cfg.L1InfoPredeployAddress, l2Seq) diff --git a/op-e2e/system/p2p/txpool_test.go b/op-e2e/system/p2p/txpool_test.go index dde89ecdefb1..bb5b3e4ef339 100644 --- a/op-e2e/system/p2p/txpool_test.go +++ b/op-e2e/system/p2p/txpool_test.go @@ -3,7 +3,6 @@ package p2p import ( "math/big" "testing" - "time" op_e2e "github.com/ethereum-optimism/optimism/op-e2e" @@ -31,7 +30,7 @@ func TestTxGossip(t *testing.T) { geth.ConnectP2P(t, seqClient, verifClient) // This prevents the below tx-sending from flaking in CI - _, err = geth.WaitForBlock(big.NewInt(10), verifClient, time.Minute) + _, err = geth.WaitForBlock(big.NewInt(10), verifClient) require.NoError(t, err) // Send a transaction to the verifier and it should be gossiped to the sequencer and included in a block. diff --git a/op-e2e/system/proofs/proposer_fp_test.go b/op-e2e/system/proofs/proposer_fp_test.go index 6be17baaaf25..971ed06a4630 100644 --- a/op-e2e/system/proofs/proposer_fp_test.go +++ b/op-e2e/system/proofs/proposer_fp_test.go @@ -40,7 +40,7 @@ func TestL2OutputSubmitterFaultProofs(t *testing.T) { require.Nil(t, err) l2Verif := sys.NodeClient("verifier") - _, err = geth.WaitForBlock(big.NewInt(6), l2Verif, 10*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second) + _, err = geth.WaitForBlock(big.NewInt(6), l2Verif) require.Nil(t, err) timeoutCh := time.After(15 * time.Second) diff --git a/op-e2e/system/proofs/proposer_l2oo_test.go b/op-e2e/system/proofs/proposer_l2oo_test.go index f82c93b64b46..bf718c616538 100644 --- a/op-e2e/system/proofs/proposer_l2oo_test.go +++ b/op-e2e/system/proofs/proposer_l2oo_test.go @@ -42,7 +42,7 @@ func TestL2OutputSubmitter(t *testing.T) { // for that block and subsequently reorgs to match what the verifier derives when running the // reconcillation process. l2Verif := sys.NodeClient("verifier") - _, err = geth.WaitForBlock(big.NewInt(6), l2Verif, 20*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second) + _, err = geth.WaitForBlock(big.NewInt(6), l2Verif) require.Nil(t, err) // Wait for batch submitter to update L2 output oracle. diff --git a/op-e2e/system/verifier/sequencer_window_test.go b/op-e2e/system/verifier/sequencer_window_test.go index aa836402e99b..04b88c54da94 100644 --- a/op-e2e/system/verifier/sequencer_window_test.go +++ b/op-e2e/system/verifier/sequencer_window_test.go @@ -48,7 +48,7 @@ func TestMissingBatchE2E(t *testing.T) { }) // Wait until the block it was first included in shows up in the safe chain on the verifier - _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif, time.Duration((sys.RollupConfig.SeqWindowSize+4)*cfg.DeployConfig.L1BlockTime)*time.Second) + _, err = geth.WaitForBlock(receipt.BlockNumber, l2Verif) require.Nil(t, err, "Waiting for block on verifier") // Assert that the transaction is not found on the verifier From f59d257e565e0d747ebe70e971cf9c73bd6eec08 Mon Sep 17 00:00:00 2001 From: Inphi Date: Thu, 24 Oct 2024 13:44:05 -0700 Subject: [PATCH 025/451] cannon: Remove memory.SetUint32 (#12617) * cannon: Remove memory.SetUint32 Remove uint32 word stores from the `mipsevm.memory` interface. `SetUint32` is inflexible due to its word-alignment constraints. This prevents tests for 32 and 64-bit VMs from using the same program counter values when writing instructions to memory. Instead, tests should use the new `testutil.StoreInstruction` utility function to write instructions to any naturally aligned memory location. * use arch.Word csats in go-ffi --- cannon/Makefile | 2 - cannon/mipsevm/memory/memory.go | 19 ------- .../multithreaded/testutil/expectations.go | 6 --- cannon/mipsevm/tests/evm_common64_test.go | 10 ++-- cannon/mipsevm/tests/evm_common_test.go | 22 ++++---- .../mipsevm/tests/evm_multithreaded64_test.go | 8 +-- .../mipsevm/tests/evm_multithreaded_test.go | 54 +++++++++---------- .../mipsevm/tests/evm_singlethreaded_test.go | 16 +++--- cannon/mipsevm/tests/fuzz_evm_common_test.go | 23 ++++---- .../tests/fuzz_evm_multithreaded_test.go | 2 +- .../tests/fuzz_evm_singlethreaded_test.go | 2 +- cannon/mipsevm/testutil/memory.go | 20 ++++++- cannon/mipsevm/testutil/state.go | 5 -- .../scripts/go-ffi/differential-testing.go | 27 +++++----- 14 files changed, 101 insertions(+), 115 deletions(-) diff --git a/cannon/Makefile b/cannon/Makefile index ccf959fc9d4c..7f553ad60abf 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -93,8 +93,6 @@ fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests # Multi-threaded tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests - # 64-bit tests - go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateDmultInsn ./mipsevm/tests .PHONY: \ cannon32-impl \ diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 209d1d431e6f..805267ee7fc4 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -192,25 +192,6 @@ func (m *Memory) pageLookup(pageIndex Word) (*CachedPage, bool) { return p, ok } -func (m *Memory) SetUint32(addr Word, v uint32) { - // addr must be aligned to WordSizeBytes bytes - if addr&arch.ExtMask != 0 { - panic(fmt.Errorf("unaligned memory access: %x", addr)) - } - - pageIndex := addr >> PageAddrSize - pageAddr := addr & PageAddrMask - p, ok := m.pageLookup(pageIndex) - if !ok { - // allocate the page if we have not already. - // Go may mmap relatively large ranges, but we only allocate the pages just in time. - p = m.AllocPage(pageIndex) - } else { - m.invalidate(addr) // invalidate this branch of memory, now that the value changed - } - binary.BigEndian.PutUint32(p.Data[pageAddr:pageAddr+4], v) -} - // SetWord stores [arch.Word] sized values at the specified address func (m *Memory) SetWord(addr Word, v Word) { // addr must be aligned to WordSizeBytes bytes diff --git a/cannon/mipsevm/multithreaded/testutil/expectations.go b/cannon/mipsevm/multithreaded/testutil/expectations.go index 113a2253aa89..16971d347cb0 100644 --- a/cannon/mipsevm/multithreaded/testutil/expectations.go +++ b/cannon/mipsevm/multithreaded/testutil/expectations.go @@ -138,12 +138,6 @@ func (e *ExpectedMTState) ExpectMemoryWordWrite(addr arch.Word, val arch.Word) { e.MemoryRoot = e.expectedMemory.MerkleRoot() } -func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr arch.Word, val uint32, addr2 arch.Word, val2 uint32) { - e.expectedMemory.SetUint32(addr, val) - e.expectedMemory.SetUint32(addr2, val2) - e.MemoryRoot = e.expectedMemory.MerkleRoot() -} - func (e *ExpectedMTState) ExpectPreemption(preState *multithreaded.State) { e.ActiveThreadId = FindNextThread(preState).ThreadId e.StepsSinceLastContextSwitch = 0 diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go index 70f59120abc0..9c340f6b095f 100644 --- a/cannon/mipsevm/tests/evm_common64_test.go +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -117,7 +117,7 @@ func TestEVMSingleStep_Operators64(t *testing.T) { state.GetRegistersRef()[rsReg] = tt.rs state.GetRegistersRef()[rtReg] = tt.rt } - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) step := state.GetStep() // Setup expectations @@ -200,7 +200,7 @@ func TestEVMSingleStep_Shift(t *testing.T) { insn = rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct state.GetRegistersRef()[rdReg] = tt.rd state.GetRegistersRef()[rtReg] = tt.rt - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) step := state.GetStep() // Setup expectations @@ -438,7 +438,7 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) { state.GetRegistersRef()[rtReg] = tt.rt state.GetRegistersRef()[baseReg] = t1 - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) state.GetMemory().SetWord(t1&arch.AddressMask, tt.memVal) step := state.GetStep() @@ -543,7 +543,7 @@ func TestEVMSingleStep_DivMult(t *testing.T) { insn := rsReg<<21 | rtReg<<16 | tt.funct state.GetRegistersRef()[rsReg] = tt.rs state.GetRegistersRef()[rtReg] = tt.rt - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) step := state.GetStep() // Setup expectations @@ -633,7 +633,7 @@ func TestEVMSingleStepBranch64(t *testing.T) { state := goVm.GetState() const rsReg = 8 // t0 insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) - state.GetMemory().SetUint32(tt.pc, insn) + testutil.StoreInstruction(state.GetMemory(), tt.pc, insn) state.GetRegistersRef()[rsReg] = Word(tt.rs) step := state.GetStep() diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index f1e08e6eda36..6f5063ec12bd 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -140,7 +140,7 @@ func TestEVMSingleStep_Jump(t *testing.T) { t.Run(testName, func(t *testing.T) { goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC)) state := goVm.GetState() - state.GetMemory().SetUint32(tt.pc, tt.insn) + testutil.StoreInstruction(state.GetMemory(), tt.pc, tt.insn) step := state.GetStep() // Setup expectations @@ -217,7 +217,7 @@ func TestEVMSingleStep_Operators(t *testing.T) { state.GetRegistersRef()[baseReg] = tt.rs state.GetRegistersRef()[rtReg] = tt.rt } - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) step := state.GetStep() // Setup expectations @@ -311,7 +311,7 @@ func TestEVMSingleStep_LoadStore(t *testing.T) { insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.imm state.GetRegistersRef()[rtReg] = tt.rt state.GetRegistersRef()[baseReg] = tt.base - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) state.GetMemory().SetWord(effAddr, tt.memVal) step := state.GetStep() @@ -365,7 +365,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { state.GetRegistersRef()[rtReg] = t2 state.GetRegistersRef()[rsReg] = Word(0xb) state.GetRegistersRef()[rdReg] = Word(0xa) - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) step := state.GetStep() // Setup expectations expected := testutil.NewExpectedState(state) @@ -420,7 +420,7 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) { state := goVm.GetState() rdReg := uint32(8) insn := rdReg<<11 | tt.funct - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) step := state.GetStep() // Setup expectations expected := testutil.NewExpectedState(state) @@ -475,7 +475,7 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.rdReg<<11 | tt.funct state.GetRegistersRef()[rtReg] = tt.rt state.GetRegistersRef()[baseReg] = tt.rs - state.GetMemory().SetUint32(0, insn) + testutil.StoreInstruction(state.GetMemory(), 0, insn) if tt.expectRevert != "" { proofData := v.ProofGenerator(t, goVm.GetState()) @@ -529,7 +529,7 @@ func TestEVMSingleStep_MthiMtlo(t *testing.T) { state := goVm.GetState() rsReg := uint32(8) insn := rsReg<<21 | tt.funct - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetRegistersRef()[rsReg] = val step := state.GetStep() // Setup expectations @@ -580,7 +580,7 @@ func TestEVM_MMap(t *testing.T) { goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithHeap(c.heap)) state := goVm.GetState() - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysMmap state.GetRegistersRef()[4] = c.address state.GetRegistersRef()[5] = c.size @@ -788,7 +788,7 @@ func TestEVMSysWriteHint(t *testing.T) { err := state.GetMemory().SetMemoryRange(arch.Word(tt.memOffset), bytes.NewReader(tt.hintData)) require.NoError(t, err) - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) step := state.GetStep() expected := testutil.NewExpectedState(state) @@ -832,7 +832,7 @@ func TestEVMFault(t *testing.T) { t.Run(testName, func(t *testing.T) { goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithNextPC(tt.nextPC)) state := goVm.GetState() - state.GetMemory().SetUint32(0, tt.insn) + testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) // set the return address ($ra) to jump into when test completes state.GetRegistersRef()[31] = testutil.EndAddr @@ -1059,7 +1059,7 @@ func TestEVMSingleStepBranch(t *testing.T) { state := goVm.GetState() const rsReg = 8 // t0 insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) - state.GetMemory().SetUint32(tt.pc, insn) + testutil.StoreInstruction(state.GetMemory(), tt.pc, insn) state.GetRegistersRef()[rsReg] = Word(tt.rs) step := state.GetStep() diff --git a/cannon/mipsevm/tests/evm_multithreaded64_test.go b/cannon/mipsevm/tests/evm_multithreaded64_test.go index a8c891b1d252..9af5f212a221 100644 --- a/cannon/mipsevm/tests/evm_multithreaded64_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded64_test.go @@ -56,7 +56,7 @@ func TestEVM_MT64_LL(t *testing.T) { step := state.GetStep() // Set up state - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetMemory().SetWord(effAddr, c.memVal) state.GetRegistersRef()[baseReg] = c.base if withExistingReservation { @@ -158,7 +158,7 @@ func TestEVM_MT64_SC(t *testing.T) { // Setup state state.GetCurrentThread().ThreadId = c.threadId - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[rtReg] = c.value state.LLReservationStatus = v.llReservationStatus @@ -232,7 +232,7 @@ func TestEVM_MT64_LLD(t *testing.T) { step := state.GetStep() // Set up state - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetMemory().SetWord(effAddr, c.memVal) state.GetRegistersRef()[baseReg] = c.base if withExistingReservation { @@ -335,7 +335,7 @@ func TestEVM_MT64_SCD(t *testing.T) { // Setup state state.GetCurrentThread().ThreadId = c.threadId - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[rtReg] = value state.LLReservationStatus = v.llReservationStatus diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 0ea9142a9168..3d873730306a 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -63,7 +63,7 @@ func TestEVM_MT_LL(t *testing.T) { // Set up state testutil.SetMemoryUint64(t, state.GetMemory(), Word(c.expectedAddr), c.memValue) - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetRegistersRef()[baseReg] = Word(c.base) if withExistingReservation { state.LLReservationStatus = multithreaded.LLStatusActive32bit @@ -160,7 +160,7 @@ func TestEVM_MT_SC(t *testing.T) { // Setup state testutil.SetMemoryUint64(t, state.GetMemory(), Word(c.expectedAddr), memValue) state.GetCurrentThread().ThreadId = c.threadId - state.GetMemory().SetUint32(state.GetPC(), insn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[rtReg] = Word(c.storeValue) state.LLReservationStatus = v.llReservationStatus @@ -224,8 +224,8 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { count Word writeLen Word preimageOffset Word - prestateMem uint32 - postateMem uint32 + prestateMem Word + postateMem Word shouldPanic bool }{ {name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_FF_FF_FF}, @@ -269,11 +269,11 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { state.GetRegistersRef()[4] = exec.FdPreimageRead state.GetRegistersRef()[5] = c.addr state.GetRegistersRef()[6] = c.count - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) state.LLReservationStatus = v.llReservationStatus state.LLAddress = llAddress state.LLOwnerThread = llOwnerThread - state.GetMemory().SetUint32(effAddr, c.prestateMem) + state.GetMemory().SetWord(effAddr, c.prestateMem) // Setup expectations expected := mttestutil.NewExpectedMTState(state) @@ -281,7 +281,7 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { expected.ActiveThread().Registers[2] = c.writeLen expected.ActiveThread().Registers[7] = 0 // no error expected.PreimageOffset += c.writeLen - expected.ExpectMemoryWriteUint32(t, effAddr, c.postateMem) + expected.ExpectMemoryWordWrite(effAddr, c.postateMem) if v.shouldClearReservation { expected.LLReservationStatus = multithreaded.LLStatusNone expected.LLAddress = 0 @@ -334,8 +334,8 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { offset int base Word effAddr Word - preMem uint32 - postMem uint32 + preMem Word + postMem Word }{ {name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF}, {name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x56_78_FF_FF}, @@ -361,8 +361,8 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { // Setup state state.GetRegistersRef()[rtReg] = rt state.GetRegistersRef()[baseReg] = c.base - state.GetMemory().SetUint32(state.GetPC(), insn) - state.GetMemory().SetUint32(c.effAddr, c.preMem) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetMemory().SetWord(c.effAddr, c.preMem) state.LLReservationStatus = v.llReservationStatus state.LLAddress = llAddress state.LLOwnerThread = llOwnerThread @@ -370,7 +370,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { // Setup expectations expected := mttestutil.NewExpectedMTState(state) expected.ExpectStep() - expected.ExpectMemoryWriteUint32(t, c.effAddr, c.postMem) + expected.ExpectMemoryWordWrite(c.effAddr, c.postMem) if v.shouldClearReservation { expected.LLReservationStatus = multithreaded.LLStatusNone expected.LLAddress = 0 @@ -411,7 +411,7 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { state := multithreaded.CreateEmptyState() - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysClone // Set syscall number state.GetRegistersRef()[4] = c.flags // Set first argument curStep := state.Step @@ -464,7 +464,7 @@ func TestEVM_SysClone_Successful(t *testing.T) { goVm, state, contracts := setup(t, i, nil) mttestutil.InitializeSingleThread(i*333, state, c.traverseRight) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysClone // the syscall number state.GetRegistersRef()[4] = exec.ValidCloneFlags // a0 - first argument, clone flags state.GetRegistersRef()[5] = stackPtr // a1 - the stack pointer @@ -527,7 +527,7 @@ func TestEVM_SysGetTID(t *testing.T) { mttestutil.InitializeSingleThread(i*789, state, false) state.GetCurrentThread().ThreadId = c.threadId - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysGetTID // Set syscall number step := state.Step @@ -570,7 +570,7 @@ func TestEVM_SysExit(t *testing.T) { goVm, state, contracts := setup(t, i*133, nil) mttestutil.SetupThreads(int64(i*1111), state, i%2 == 0, c.threadCount, 0) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysExit // Set syscall number state.GetRegistersRef()[4] = Word(exitCode) // The first argument (exit code) step := state.Step @@ -678,7 +678,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { goVm, state, contracts := setup(t, i*1234, nil) step := state.GetStep() - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.Memory.SetWord(c.effAddr, c.actualValue) state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number state.GetRegistersRef()[4] = c.addressParam @@ -749,7 +749,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount) step := state.Step - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number state.GetRegistersRef()[4] = c.addressParam state.GetRegistersRef()[5] = exec.FutexWakePrivate @@ -834,7 +834,7 @@ func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { goVm, state, contracts := setup(t, int(op), nil) step := state.GetStep() - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number state.GetRegistersRef()[5] = op @@ -889,7 +889,7 @@ func runPreemptSyscall(t *testing.T, syscallName string, syscallNum uint32) { goVm, state, contracts := setup(t, i*789, nil) mttestutil.SetupThreads(int64(i*3259), state, traverseRight, c.activeThreads, c.inactiveThreads) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = Word(syscallNum) // Set syscall number step := state.Step @@ -919,7 +919,7 @@ func TestEVM_SysOpen(t *testing.T) { goVm, state, contracts := setup(t, 5512, nil) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysOpen // Set syscall number step := state.Step @@ -944,7 +944,7 @@ func TestEVM_SysGetPID(t *testing.T) { var tracer *tracing.Hooks goVm, state, contracts := setup(t, 1929, nil) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysGetpid // Set syscall number step := state.Step @@ -1029,7 +1029,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) { llOwnerThread = state.GetCurrentThread().ThreadId + 1 } - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysClockGetTime // Set syscall number state.GetRegistersRef()[4] = clkid // a0 state.GetRegistersRef()[5] = c.timespecAddr // a1 @@ -1073,7 +1073,7 @@ func TestEVM_SysClockGettimeNonMonotonic(t *testing.T) { goVm, state, contracts := setup(t, 2101, nil) timespecAddr := Word(0x1000) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysClockGetTime // Set syscall number state.GetRegistersRef()[4] = 0xDEAD // a0 - invalid clockid state.GetRegistersRef()[5] = timespecAddr // a1 @@ -1136,7 +1136,7 @@ func TestEVM_NoopSyscall(t *testing.T) { t.Run(noopName, func(t *testing.T) { goVm, state, contracts := setup(t, int(noopVal), nil) - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number step := state.Step @@ -1183,7 +1183,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) { t.Parallel() goVm, state, contracts := setup(t, i*3434, nil) // Setup basic getThreadId syscall instruction - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = Word(syscallNum) proofData := multiThreadedProofGenerator(t, state) // Set up post-state expectations @@ -1587,7 +1587,7 @@ func TestEVM_SchedQuantumThreshold(t *testing.T) { t.Run(c.name, func(t *testing.T) { goVm, state, contracts := setup(t, i*789, nil) // Setup basic getThreadId syscall instruction - state.Memory.SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysGetTID // Set syscall number state.StepsSinceLastContextSwitch = c.stepsSinceLastContextSwitch step := state.Step diff --git a/cannon/mipsevm/tests/evm_singlethreaded_test.go b/cannon/mipsevm/tests/evm_singlethreaded_test.go index cf19a83a14ce..159bc2bf4dbd 100644 --- a/cannon/mipsevm/tests/evm_singlethreaded_test.go +++ b/cannon/mipsevm/tests/evm_singlethreaded_test.go @@ -42,7 +42,7 @@ func TestEVM_LL(t *testing.T) { insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) state := goVm.GetState() - state.GetMemory().SetUint32(pc, insn) + testutil.StoreInstruction(state.GetMemory(), pc, insn) state.GetMemory().SetWord(c.effAddr, c.value) state.GetRegistersRef()[baseReg] = c.base step := state.GetStep() @@ -92,7 +92,7 @@ func TestEVM_SC(t *testing.T) { insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) state := goVm.GetState() - state.GetMemory().SetUint32(pc, insn) + testutil.StoreInstruction(state.GetMemory(), pc, insn) state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[rtReg] = c.value step := state.GetStep() @@ -103,7 +103,7 @@ func TestEVM_SC(t *testing.T) { expected.PC = pc + 4 expected.NextPC = pc + 8 expectedMemory := memory.NewMemory() - expectedMemory.SetUint32(pc, insn) + testutil.StoreInstruction(expectedMemory, pc, insn) expectedMemory.SetWord(c.effAddr, c.value) expected.MemoryRoot = expectedMemory.MerkleRoot() if rtReg != 0 { @@ -135,8 +135,8 @@ func TestEVM_SysRead_Preimage(t *testing.T) { count Word writeLen Word preimageOffset Word - prestateMem uint32 - postateMem uint32 + prestateMem Word + postateMem Word shouldPanic bool }{ {name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_FF_FF_FF}, @@ -170,8 +170,8 @@ func TestEVM_SysRead_Preimage(t *testing.T) { state.GetRegistersRef()[4] = exec.FdPreimageRead state.GetRegistersRef()[5] = c.addr state.GetRegistersRef()[6] = c.count - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) - state.GetMemory().SetUint32(effAddr, c.prestateMem) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) + state.GetMemory().SetWord(effAddr, c.prestateMem) // Setup expectations expected := testutil.NewExpectedState(state) @@ -179,7 +179,7 @@ func TestEVM_SysRead_Preimage(t *testing.T) { expected.Registers[2] = c.writeLen expected.Registers[7] = 0 // no error expected.PreimageOffset += c.writeLen - expected.ExpectMemoryWrite(effAddr, c.postateMem) + expected.ExpectMemoryWriteWord(effAddr, c.postateMem) if c.shouldPanic { require.Panics(t, func() { _, _ = goVm.Step(true) }) diff --git a/cannon/mipsevm/tests/fuzz_evm_common_test.go b/cannon/mipsevm/tests/fuzz_evm_common_test.go index 8c7081456af8..2c992b5b7a3f 100644 --- a/cannon/mipsevm/tests/fuzz_evm_common_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_common_test.go @@ -2,7 +2,6 @@ package tests import ( "bytes" - "encoding/binary" "math" "os" "testing" @@ -28,7 +27,7 @@ func FuzzStateSyscallBrk(f *testing.F) { goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() state.GetRegistersRef()[2] = arch.SysBrk - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) step := state.GetStep() expected := testutil.NewExpectedState(state) @@ -68,7 +67,7 @@ func FuzzStateSyscallMmap(f *testing.F) { state.GetRegistersRef()[2] = arch.SysMmap state.GetRegistersRef()[4] = addr state.GetRegistersRef()[5] = siz - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) expected := testutil.NewExpectedState(state) expected.Step += 1 @@ -114,7 +113,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { state := goVm.GetState() state.GetRegistersRef()[2] = arch.SysExitGroup state.GetRegistersRef()[4] = Word(exitCode) - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) step := state.GetStep() expected := testutil.NewExpectedState(state) @@ -144,7 +143,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { state.GetRegistersRef()[2] = arch.SysFcntl state.GetRegistersRef()[4] = fd state.GetRegistersRef()[5] = cmd - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) step := state.GetStep() expected := testutil.NewExpectedState(state) @@ -205,7 +204,7 @@ func FuzzStateHintRead(f *testing.F) { state.GetRegistersRef()[4] = exec.FdHintRead state.GetRegistersRef()[5] = addr state.GetRegistersRef()[6] = count - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) step := state.GetStep() expected := testutil.NewExpectedState(state) @@ -249,8 +248,8 @@ func FuzzStatePreimageRead(f *testing.F) { state.GetRegistersRef()[4] = exec.FdPreimageRead state.GetRegistersRef()[5] = addr state.GetRegistersRef()[6] = count - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) - state.GetMemory().SetUint32(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:])) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) + state.GetMemory().SetWord(effAddr, arch.ByteOrderWord.Word(preexistingMemoryVal[:])) step := state.GetStep() alignment := addr & arch.ExtMask @@ -275,7 +274,7 @@ func FuzzStatePreimageRead(f *testing.F) { // Expect a memory write expectedMemory := preexistingMemoryVal copy(expectedMemory[alignment:], preimageData[preimageOffset:preimageOffset+writeLen]) - expected.ExpectMemoryWrite(effAddr, binary.BigEndian.Uint32(expectedMemory[:])) + expected.ExpectMemoryWriteWord(effAddr, arch.ByteOrderWord.Word(expectedMemory[:])) } stepWitness, err := goVm.Step(true) @@ -331,7 +330,7 @@ func FuzzStateHintWrite(f *testing.F) { step := state.GetStep() err := state.GetMemory().SetMemoryRange(addr, bytes.NewReader(hintData[int(lastHintLen):])) require.NoError(t, err) - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) // Set up expectations expected := testutil.NewExpectedState(state) @@ -394,8 +393,8 @@ func FuzzStatePreimageWrite(f *testing.F) { state.GetRegistersRef()[4] = exec.FdPreimageWrite state.GetRegistersRef()[5] = addr state.GetRegistersRef()[6] = count - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) - state.GetMemory().SetUint32(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:])) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) + state.GetMemory().SetWord(effAddr, arch.ByteOrderWord.Word(preexistingMemoryVal[:])) step := state.GetStep() expectBytesWritten := count diff --git a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go index d898391ba4d9..09ad014189fe 100644 --- a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go @@ -27,7 +27,7 @@ func FuzzStateSyscallCloneMT(f *testing.F) { // Setup state.NextThreadId = nextThreadId - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysClone state.GetRegistersRef()[4] = exec.ValidCloneFlags state.GetRegistersRef()[5] = stackPtr diff --git a/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go index 7d2c8ff6f28a..4c6542269a72 100644 --- a/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go @@ -17,7 +17,7 @@ func FuzzStateSyscallCloneST(f *testing.F) { goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() state.GetRegistersRef()[2] = arch.SysClone - state.GetMemory().SetUint32(state.GetPC(), syscallInsn) + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) step := state.GetStep() expected := testutil.NewExpectedState(state) diff --git a/cannon/mipsevm/testutil/memory.go b/cannon/mipsevm/testutil/memory.go index 7d90893d6b9f..0c6fddf8f140 100644 --- a/cannon/mipsevm/testutil/memory.go +++ b/cannon/mipsevm/testutil/memory.go @@ -1,6 +1,12 @@ package testutil -import "encoding/binary" +import ( + "encoding/binary" + "fmt" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" +) func Uint32ToBytes(val uint32) []byte { data := make([]byte, 4) @@ -15,3 +21,15 @@ func Uint64ToBytes(val uint64) []byte { return data } + +// StoreInstruction writes a 4-byte instruction to memory +func StoreInstruction(mem *memory.Memory, pc Word, insn uint32) { + if pc&0x3 != 0 { + panic(fmt.Errorf("unaligned memory access: %x", pc)) + } + exec.StoreSubWord(mem, pc, 4, Word(insn), new(noopMemTracker)) +} + +type noopMemTracker struct{} + +func (n *noopMemTracker) TrackMemAccess(Word) {} diff --git a/cannon/mipsevm/testutil/state.go b/cannon/mipsevm/testutil/state.go index 10abb59be8d7..42e4b8851ac3 100644 --- a/cannon/mipsevm/testutil/state.go +++ b/cannon/mipsevm/testutil/state.go @@ -177,11 +177,6 @@ func (e *ExpectedState) ExpectStep() { e.NextPC += 4 } -func (e *ExpectedState) ExpectMemoryWrite(addr arch.Word, val uint32) { - e.expectedMemory.SetUint32(addr, val) - e.MemoryRoot = e.expectedMemory.MerkleRoot() -} - func (e *ExpectedState) ExpectMemoryWriteWord(addr arch.Word, val arch.Word) { e.expectedMemory.SetWord(addr, val) e.MemoryRoot = e.expectedMemory.MerkleRoot() diff --git a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go index 5eef7f138977..b81bbeb2b153 100644 --- a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go +++ b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -369,7 +370,7 @@ func DiffTestUtils() { checkErr(err, "Error decoding addr") insn, err := strconv.ParseUint(args[2], 10, 32) checkErr(err, "Error decoding insn") - mem.SetUint32(uint32(pc), uint32(insn)) + mem.SetWord(arch.Word(pc), arch.Word(insn)) var insnProof, memProof [896]byte if len(args) >= 5 { @@ -377,18 +378,18 @@ func DiffTestUtils() { checkErr(err, "Error decoding memAddr") memValue, err := strconv.ParseUint(args[4], 10, 32) checkErr(err, "Error decoding memValue") - mem.SetUint32(uint32(memAddr), uint32(memValue)) - memProof = mem.MerkleProof(uint32(memAddr)) + mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memProof = mem.MerkleProof(arch.Word(memAddr)) } if len(args) == 7 { memAddr, err := strconv.ParseUint(args[5], 10, 32) checkErr(err, "Error decoding memAddr") memValue, err := strconv.ParseUint(args[6], 10, 32) checkErr(err, "Error decoding memValue") - mem.SetUint32(uint32(memAddr), uint32(memValue)) - memProof = mem.MerkleProof(uint32(memAddr)) + mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memProof = mem.MerkleProof(arch.Word(memAddr)) } - insnProof = mem.MerkleProof(uint32(pc)) + insnProof = mem.MerkleProof(arch.Word(pc)) output := struct { MemRoot common.Hash @@ -411,18 +412,18 @@ func DiffTestUtils() { checkErr(err, "Error decoding addr") insn, err := strconv.ParseUint(args[2], 10, 32) checkErr(err, "Error decoding insn") - mem.SetUint32(uint32(pc), uint32(insn)) + mem.SetWord(arch.Word(pc), arch.Word(insn)) var memProof [896]byte memAddr, err := strconv.ParseUint(args[3], 10, 32) checkErr(err, "Error decoding memAddr") memValue, err := strconv.ParseUint(args[4], 10, 32) checkErr(err, "Error decoding memValue") - mem.SetUint32(uint32(memAddr), uint32(memValue)) + mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) memAddr2, err := strconv.ParseUint(args[5], 10, 32) checkErr(err, "Error decoding memAddr") - memProof = mem.MerkleProof(uint32(memAddr2)) + memProof = mem.MerkleProof(arch.Word(memAddr2)) output := struct { MemRoot common.Hash @@ -444,18 +445,18 @@ func DiffTestUtils() { checkErr(err, "Error decoding addr") insn, err := strconv.ParseUint(args[2], 10, 32) checkErr(err, "Error decoding insn") - mem.SetUint32(uint32(pc), uint32(insn)) + mem.SetWord(arch.Word(pc), arch.Word(insn)) var insnProof, memProof [896]byte memAddr, err := strconv.ParseUint(args[3], 10, 32) checkErr(err, "Error decoding memAddr") memValue, err := strconv.ParseUint(args[4], 10, 32) checkErr(err, "Error decoding memValue") - mem.SetUint32(uint32(memAddr), uint32(memValue)) + mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) // Compute a valid proof for the root, but for the wrong leaves. - memProof = mem.MerkleProof(uint32(memAddr + 32)) - insnProof = mem.MerkleProof(uint32(pc + 32)) + memProof = mem.MerkleProof(arch.Word(memAddr + 32)) + insnProof = mem.MerkleProof(arch.Word(pc + 32)) output := struct { MemRoot common.Hash From 1b7a414001c48e90c036d5e5d94fae8e8f1d1275 Mon Sep 17 00:00:00 2001 From: Inphi Date: Thu, 24 Oct 2024 14:13:29 -0700 Subject: [PATCH 026/451] cannon: Simplify load/stores with helper functions (#12599) * cannon: Simplify load/stores with helper functions * use subword utils in MIPS.sol * lint MIPS.sol * add natspec to MIPSInstructions.sol * use updateSubWord in MIPSInstructions.sol * bump MIPS contract semver * fix nits --- cannon/mipsevm/exec/mips_instructions.go | 112 +++++++----------- cannon/mipsevm/tests/evm_common64_test.go | 26 ++-- packages/contracts-bedrock/semver-lock.json | 8 +- .../contracts-bedrock/src/cannon/MIPS.sol | 4 +- .../contracts-bedrock/src/cannon/MIPS2.sol | 4 +- .../src/cannon/libraries/MIPSInstructions.sol | 85 +++++++++++-- 6 files changed, 138 insertions(+), 101 deletions(-) diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index b99f519fe2f9..9cc94e98e50c 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -343,12 +343,9 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x0F: // lui return SignExtend(rt<<16, 32) case 0x20: // lb - msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit - return SignExtend((mem>>(msb-uint32(rs&arch.ExtMask)*8))&0xFF, 8) + return SelectSubWord(rs, mem, 1, true) case 0x21: // lh - msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit - mask := Word(arch.ExtMask - 1) - return SignExtend((mem>>(msb-uint32(rs&mask)*8))&0xFFFF, 16) + return SelectSubWord(rs, mem, 2, true) case 0x22: // lwl if arch.IsMips32 { val := mem << ((rs & 3) * 8) @@ -356,26 +353,17 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) } else { // similar to the above mips32 implementation but loads are constrained to the nearest 4-byte memory word - shift := 32 - ((rs & 0x4) << 3) - w := uint32(mem >> shift) - val := uint64(w << ((rs & 3) * 8)) + w := uint32(SelectSubWord(rs, mem, 4, false)) + val := w << ((rs & 3) * 8) mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) return SignExtend(((rt & ^mask)|Word(val))&0xFFFFFFFF, 32) } case 0x23: // lw - if arch.IsMips32 { - return mem - } else { - // TODO(#12562): Simplify using LoadSubWord - return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) - } + return SelectSubWord(rs, mem, 4, true) case 0x24: // lbu - msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit - return (mem >> (msb - uint32(rs&arch.ExtMask)*8)) & 0xFF + return SelectSubWord(rs, mem, 1, false) case 0x25: // lhu - msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit - mask := Word(arch.ExtMask - 1) - return (mem >> (msb - uint32(rs&mask)*8)) & 0xFFFF + return SelectSubWord(rs, mem, 2, false) case 0x26: // lwr if arch.IsMips32 { val := mem >> (24 - (rs&3)*8) @@ -383,8 +371,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) } else { // similar to the above mips32 implementation but constrained to the nearest 4-byte memory word - shift := 32 - ((rs & 0x4) << 3) - w := uint32(mem >> shift) + w := uint32(SelectSubWord(rs, mem, 4, false)) val := w >> (24 - (rs&3)*8) mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) lwrResult := ((uint32(rt) & ^mask) | val) & 0xFFFFFFFF @@ -397,17 +384,9 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem } } case 0x28: // sb - msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit - val := (rt & 0xFF) << (msb - uint32(rs&arch.ExtMask)*8) - mask := ^Word(0) ^ Word(0xFF<<(msb-uint32(rs&arch.ExtMask)*8)) - return (mem & mask) | val + return UpdateSubWord(rs, mem, 1, rt) case 0x29: // sh - msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit - rsMask := Word(arch.ExtMask - 1) // 2 for 32-bit and 6 for 64-bit - sl := msb - uint32(rs&rsMask)*8 - val := (rt & 0xFFFF) << sl - mask := ^Word(0) ^ Word(0xFFFF<> ((rs & 3) * 8) @@ -420,14 +399,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return (mem & Word(^mask)) | val } case 0x2b: // sw - if arch.IsMips32 { - return rt - } else { - sl := 32 - ((rs & 0x4) << 3) - val := (rt & 0xFFFFFFFF) << sl - mask := Word(0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFFFFFF<> shift) + w := uint32(SelectSubWord(rs, mem, 4, false)) val := rt << (24 - (rs&3)*8) mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8) swrResult := (w & ^mask) | uint32(val) - // merge with the untouched bytes in mem - if shift > 0 { - return (Word(swrResult) << 32) | (mem & Word(^uint32(0))) // nolint: staticcheck - } else { - memMask := uint64(0xFF_FF_FF_FF_00_00_00_00) - return (mem & Word(memMask)) | Word(swrResult & ^uint32(0)) - } + return UpdateSubWord(rs, mem, 4, Word(swrResult)) } // MIPS64 @@ -663,45 +628,56 @@ func HandleRd(cpu *mipsevm.CpuScalars, registers *[32]Word, storeReg Word, val W return nil } -func LoadSubWord(memory *memory.Memory, addr Word, byteLength Word, signExtend bool, memoryTracker MemTracker) Word { +// LoadSubWord loads a subword of byteLength size from memory based on the low-order bits of vaddr +func LoadSubWord(memory *memory.Memory, vaddr Word, byteLength Word, signExtend bool, memoryTracker MemTracker) Word { // Pull data from memory - effAddr := (addr) & arch.AddressMask + effAddr := (vaddr) & arch.AddressMask memoryTracker.TrackMemAccess(effAddr) mem := memory.GetWord(effAddr) - // Extract a sub-word based on the low-order bits in addr - dataMask, bitOffset, bitLength := calculateSubWordMaskAndOffset(addr, byteLength) - retVal := (mem >> bitOffset) & dataMask - if signExtend { - retVal = SignExtend(retVal, bitLength) - } - - return retVal + return SelectSubWord(vaddr, mem, byteLength, signExtend) } -func StoreSubWord(memory *memory.Memory, addr Word, byteLength Word, value Word, memoryTracker MemTracker) { +// StoreSubWord stores a [Word] that has been updated by the specified value at bit positions determined by the vaddr +func StoreSubWord(memory *memory.Memory, vaddr Word, byteLength Word, value Word, memoryTracker MemTracker) { // Pull data from memory - effAddr := (addr) & arch.AddressMask + effAddr := (vaddr) & arch.AddressMask memoryTracker.TrackMemAccess(effAddr) mem := memory.GetWord(effAddr) // Modify isolated sub-word within mem - dataMask, bitOffset, _ := calculateSubWordMaskAndOffset(addr, byteLength) + newMemVal := UpdateSubWord(vaddr, mem, byteLength, value) + memory.SetWord(effAddr, newMemVal) +} + +// SelectSubWord selects a subword of byteLength size contained in memWord based on the low-order bits of vaddr +// This is the nearest subword that is naturally aligned by the specified byteLength +func SelectSubWord(vaddr Word, memWord Word, byteLength Word, signExtend bool) Word { + // Extract a sub-word based on the low-order bits in vaddr + dataMask, bitOffset, bitLength := calculateSubWordMaskAndOffset(vaddr, byteLength) + retVal := (memWord >> bitOffset) & dataMask + if signExtend { + retVal = SignExtend(retVal, bitLength) + } + return retVal +} + +// UpdateSubWord returns a [Word] that has been updated by the specified value at bit positions determined by the vaddr +func UpdateSubWord(vaddr Word, memWord Word, byteLength Word, value Word) Word { + dataMask, bitOffset, _ := calculateSubWordMaskAndOffset(vaddr, byteLength) subWordValue := dataMask & value memUpdateMask := dataMask << bitOffset - newMemVal := subWordValue<> (arch.WordSize - bitLength) - // Figure out sub-word index based on the low-order bits in addr - byteIndexMask := addr & arch.ExtMask & ^(byteLength - 1) + // Figure out sub-word index based on the low-order bits in vaddr + byteIndexMask := vaddr & arch.ExtMask & ^(byteLength - 1) maxByteShift := arch.WordSizeBytes - byteLength - byteIndex := addr & byteIndexMask + byteIndex := vaddr & byteIndexMask bitOffset = (maxByteShift - byteIndex) << 3 return dataMask, bitOffset, bitLength diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go index 9c340f6b095f..cf51ad09038c 100644 --- a/cannon/mipsevm/tests/evm_common64_test.go +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -322,19 +322,19 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) { {name: "lwr sign-extended imm 7 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x00_00_00_00_71_B1_C1_D1)}, // lwr $t0, 7($t1) {name: "lwr sign-extended imm 7 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_A1_B1_C1_D1)}, // lwr $t0, 7($t1) - {name: "sb offset=0", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x88_00_00_00_00_00_00_00)}, // sb $t0, 0($t1) - {name: "sb offset=1", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, expectMemVal: Word(0x00_88_00_00_00_00_00_00)}, // sb $t0, 1($t1) - {name: "sb offset=2", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_88_00_00_00_00_00)}, // sb $t0, 2($t1) - {name: "sb offset=3", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, expectMemVal: Word(0x00_00_00_88_00_00_00_00)}, // sb $t0, 3($t1) - {name: "sb offset=4", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_88_00_00_00)}, // sb $t0, 4($t1) - {name: "sb offset=5", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, expectMemVal: Word(0x00_00_00_00_00_88_00_00)}, // sb $t0, 5($t1) - {name: "sb offset=6", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_88_00)}, // sb $t0, 6($t1) - {name: "sb offset=7", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, expectMemVal: Word(0x00_00_00_00_00_00_00_88)}, // sb $t0, 7($t1) - - {name: "sh offset=0", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x77_88_00_00_00_00_00_00)}, // sh $t0, 0($t1) - {name: "sh offset=2", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_77_88_00_00_00_00)}, // sh $t0, 2($t1) - {name: "sh offset=4", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_77_88_00_00)}, // sh $t0, 4($t1) - {name: "sh offset=6", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_77_88)}, // sh $t0, 6($t1) + {name: "sb offset=0", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x88_BB_CC_DD_A1_B1_C1_D1)}, // sb $t0, 0($t1) + {name: "sb offset=1", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, expectMemVal: Word(0xAA_88_CC_DD_A1_B1_C1_D1)}, // sb $t0, 1($t1) + {name: "sb offset=2", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0xAA_BB_88_DD_A1_B1_C1_D1)}, // sb $t0, 2($t1) + {name: "sb offset=3", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, expectMemVal: Word(0xAA_BB_CC_88_A1_B1_C1_D1)}, // sb $t0, 3($t1) + {name: "sb offset=4", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_88_B1_C1_D1)}, // sb $t0, 4($t1) + {name: "sb offset=5", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, expectMemVal: Word(0xAA_BB_CC_DD_A1_88_C1_D1)}, // sb $t0, 5($t1) + {name: "sb offset=6", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_88_D1)}, // sb $t0, 6($t1) + {name: "sb offset=7", opcode: uint32(0x28), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_C1_88)}, // sb $t0, 7($t1) + + {name: "sh offset=0", opcode: uint32(0x29), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x77_88_CC_DD_A1_B1_C1_D1)}, // sh $t0, 0($t1) + {name: "sh offset=2", opcode: uint32(0x29), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0xAA_BB_77_88_A1_B1_C1_D1)}, // sh $t0, 2($t1) + {name: "sh offset=4", opcode: uint32(0x29), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_77_88_C1_D1)}, // sh $t0, 4($t1) + {name: "sh offset=6", opcode: uint32(0x29), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_77_88)}, // sh $t0, 6($t1) {name: "swl offset=0", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // swl $t0, 0($t1) {name: "swl offset=1", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 1, expectMemVal: Word(0xAA_55_66_77_A1_B1_C1_D1)}, // swl $t0, 1($t1) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 26828dd40f8e..896b9534439f 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -140,12 +140,12 @@ "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, "src/cannon/MIPS.sol": { - "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2", - "sourceCodeHash": "0x3c2e5d6d39b29a60dcd1a776363518c87fcfef9a8d80e38696f56b6eec5bd53a" + "initCodeHash": "0x94f2e8f7818a990c009cccb501216a7f6df29b05d8f178cfff10a40288bf588e", + "sourceCodeHash": "0x786d4947488a771a426cc38de307ae99b2c2af1efca38b7655c60be7c019371f" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0x5c292882075bd06e68b89119be9096040bc913f51bb878ce4a9dfdd674330dd5", - "sourceCodeHash": "0x17c7b0edb9d20932eaf1b038e3e05e457f0461d2c8691ba1940fb4c2b0dfd123" + "initCodeHash": "0x3744036fa240c7d57f39307c0bf27cb7027d05d6b4f52142d5122b6e538ee0b2", + "sourceCodeHash": "0x5f79a0f99a288d570df243ea9560e67a319d1685b3209ae457fc714a76ff2908" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 38989cfc7b7b..7e16d48a3dbb 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -44,8 +44,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.5 - string public constant version = "1.2.1-beta.5"; + /// @custom:semver 1.2.1-beta.6 + string public constant version = "1.2.1-beta.6"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 3f5044f14e04..b01b5a78d3de 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.18 - string public constant version = "1.0.0-beta.18"; + /// @custom:semver 1.0.0-beta.19 + string public constant version = "1.0.0-beta.19"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol index eab4a61db65a..32ec9007fd7e 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol @@ -379,11 +379,11 @@ library MIPSInstructions { } // lb else if (_opcode == 0x20) { - return signExtend((_mem >> (24 - (_rs & 3) * 8)) & 0xFF, 8); + return selectSubWord(_rs, _mem, 1, true); } // lh else if (_opcode == 0x21) { - return signExtend((_mem >> (16 - (_rs & 2) * 8)) & 0xFFFF, 16); + return selectSubWord(_rs, _mem, 2, true); } // lwl else if (_opcode == 0x22) { @@ -393,15 +393,15 @@ library MIPSInstructions { } // lw else if (_opcode == 0x23) { - return _mem; + return selectSubWord(_rs, _mem, 4, true); } // lbu else if (_opcode == 0x24) { - return (_mem >> (24 - (_rs & 3) * 8)) & 0xFF; + return selectSubWord(_rs, _mem, 1, false); } // lhu else if (_opcode == 0x25) { - return (_mem >> (16 - (_rs & 2) * 8)) & 0xFFFF; + return selectSubWord(_rs, _mem, 2, false); } // lwr else if (_opcode == 0x26) { @@ -411,15 +411,11 @@ library MIPSInstructions { } // sb else if (_opcode == 0x28) { - uint32 val = (_rt & 0xFF) << (24 - (_rs & 3) * 8); - uint32 mask = 0xFFFFFFFF ^ uint32(0xFF << (24 - (_rs & 3) * 8)); - return (_mem & mask) | val; + return updateSubWord(_rs, _mem, 1, _rt); } // sh else if (_opcode == 0x29) { - uint32 val = (_rt & 0xFFFF) << (16 - (_rs & 2) * 8); - uint32 mask = 0xFFFFFFFF ^ uint32(0xFFFF << (16 - (_rs & 2) * 8)); - return (_mem & mask) | val; + return updateSubWord(_rs, _mem, 2, _rt); } // swl else if (_opcode == 0x2a) { @@ -429,7 +425,7 @@ library MIPSInstructions { } // sw else if (_opcode == 0x2b) { - return _rt; + return updateSubWord(_rs, _mem, 4, _rt); } // swr else if (_opcode == 0x2e) { @@ -667,4 +663,69 @@ library MIPSInstructions { _cpu.nextPC = _cpu.nextPC + 4; } } + + /// @notice Selects a subword of byteLength size contained in memWord based on the low-order bits of vaddr + /// @param _vaddr The virtual address of the the subword. + /// @param _memWord The full word to select a subword from. + /// @param _byteLength The size of the subword. + /// @param _signExtend Whether to sign extend the selected subwrod. + function selectSubWord( + uint32 _vaddr, + uint32 _memWord, + uint32 _byteLength, + bool _signExtend + ) + internal + pure + returns (uint32 retval_) + { + (uint32 dataMask, uint32 bitOffset, uint32 bitLength) = calculateSubWordMaskAndOffset(_vaddr, _byteLength); + retval_ = (_memWord >> bitOffset) & dataMask; + if (_signExtend) { + retval_ = signExtend(retval_, bitLength); + } + return retval_; + } + + /// @notice Returns a word that has been updated by the specified subword at bit positions determined by the virtual + /// address + /// @param _vaddr The virtual address of the subword. + /// @param _memWord The full word to update. + /// @param _byteLength The size of the subword. + /// @param _value The subword that updates _memWord. + function updateSubWord( + uint32 _vaddr, + uint32 _memWord, + uint32 _byteLength, + uint32 _value + ) + internal + pure + returns (uint32 word_) + { + (uint32 dataMask, uint32 bitOffset,) = calculateSubWordMaskAndOffset(_vaddr, _byteLength); + uint32 subWordValue = dataMask & _value; + uint32 memUpdateMask = dataMask << bitOffset; + return subWordValue << bitOffset | (~memUpdateMask) & _memWord; + } + + function calculateSubWordMaskAndOffset( + uint32 _vaddr, + uint32 _byteLength + ) + internal + pure + returns (uint32 dataMask_, uint32 bitOffset_, uint32 bitLength_) + { + uint32 bitLength = _byteLength << 3; + uint32 dataMask = ~uint32(0) >> (32 - bitLength); + + // Figure out sub-word index based on the low-order bits in vaddr + uint32 byteIndexMask = _vaddr & 0x3 & ~(_byteLength - 1); + uint32 maxByteShift = 4 - _byteLength; + uint32 byteIndex = _vaddr & byteIndexMask; + uint32 bitOffset = (maxByteShift - byteIndex) << 3; + + return (dataMask, bitOffset, bitLength); + } } From dda07ca0997899f2dfbcce49bf77e7c69f506ae1 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Fri, 25 Oct 2024 08:49:30 +1000 Subject: [PATCH 027/451] op-e2e: Fix how output_cannon_test waits for resolved game status (#12630) --- op-e2e/faultproofs/output_cannon_test.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/op-e2e/faultproofs/output_cannon_test.go b/op-e2e/faultproofs/output_cannon_test.go index 80dea8bfb8dd..0b0aea509feb 100644 --- a/op-e2e/faultproofs/output_cannon_test.go +++ b/op-e2e/faultproofs/output_cannon_test.go @@ -252,9 +252,7 @@ func testOutputCannonDefendStep(t *testing.T, allocType config.AllocType) { sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) require.NoError(t, wait.ForNextBlock(ctx, l1Client)) - game.WaitForInactivity(ctx, 10, true) - game.LogGameData(ctx) - require.EqualValues(t, gameTypes.GameStatusChallengerWon, game.Status(ctx)) + game.WaitForGameStatus(ctx, gameTypes.GameStatusChallengerWon) } func TestOutputCannonStepWithLargePreimage_Standard(t *testing.T) { @@ -502,9 +500,7 @@ func testOutputCannonProposedOutputRootValid(t *testing.T, allocType config.Allo sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) require.NoError(t, wait.ForNextBlock(ctx, l1Client)) - game.WaitForInactivity(ctx, 10, true) - game.LogGameData(ctx) - require.EqualValues(t, gameTypes.GameStatusDefenderWon, game.Status(ctx)) + game.WaitForGameStatus(ctx, gameTypes.GameStatusDefenderWon) }) } } @@ -1003,9 +999,7 @@ func testOutputCannonHonestSafeTraceExtensionValidRoot(t *testing.T, allocType c sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) require.NoError(t, wait.ForNextBlock(ctx, l1Client)) - game.WaitForInactivity(ctx, 10, true) - game.LogGameData(ctx) - require.EqualValues(t, gameTypes.GameStatusDefenderWon, game.Status(ctx)) + game.WaitForGameStatus(ctx, gameTypes.GameStatusDefenderWon) } func TestOutputCannonHonestSafeTraceExtension_InvalidRoot_Standard(t *testing.T) { @@ -1052,9 +1046,7 @@ func testOutputCannonHonestSafeTraceExtensionInvalidRoot(t *testing.T, allocType sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) require.NoError(t, wait.ForNextBlock(ctx, l1Client)) - game.WaitForInactivity(ctx, 10, true) - game.LogGameData(ctx) - require.EqualValues(t, gameTypes.GameStatusChallengerWon, game.Status(ctx)) + game.WaitForGameStatus(ctx, gameTypes.GameStatusChallengerWon) } func TestAgreeFirstBlockWithOriginOf1_Standard(t *testing.T) { From d317e96c2add4a4e7fae72d6637ad241457f16c7 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 25 Oct 2024 08:06:52 +0700 Subject: [PATCH 028/451] feat(ct): add semgrep rule to use encodeCall (#12626) * feat(ct): add semgrep rule to use encodeCall encodeCall is almost always better than encodeWithSelector because it maintains type safety. Prefer encodeCall unless encodeWithSelector is actually necessary. * maint(ct): update contracts to use encodeCall Updates a number of contracts to use encodeCall instead of encodeWithSelector where possible. --- .../scripts/DeployImplementations.s.sol | 8 ++------ packages/contracts-bedrock/semver-lock.json | 8 ++++---- .../contracts-bedrock/src/L1/L1ERC721Bridge.sol | 8 ++++---- .../contracts-bedrock/src/L2/L2ERC721Bridge.sol | 8 ++++---- semgrep/sol-rules.yaml | 14 ++++++++++++++ 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol index 3179ea3d6426..af5f75e6125c 100644 --- a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol @@ -570,9 +570,7 @@ contract DeployImplementations is Script { OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); vm.startBroadcast(msg.sender); - proxy.upgradeToAndCall( - address(opcmImpl), abi.encodeWithSelector(opcmImpl.initialize.selector, initializerInputs) - ); + proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); proxy.changeAdmin(address(opcmProxyOwner)); // transfer ownership of Proxy contract to the ProxyAdmin contract vm.stopBroadcast(); @@ -1146,9 +1144,7 @@ contract DeployImplementationsInterop is DeployImplementations { OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); vm.startBroadcast(msg.sender); - proxy.upgradeToAndCall( - address(opcmImpl), abi.encodeWithSelector(opcmImpl.initialize.selector, initializerInputs) - ); + proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); proxy.changeAdmin(opcmProxyOwner); // transfer ownership of Proxy contract to the ProxyAdmin contract vm.stopBroadcast(); diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 896b9534439f..3ba87ad738b0 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -12,8 +12,8 @@ "sourceCodeHash": "0x848ec3774be17bcc8ba65a23d08e35e979b3f39f9d2ac8a810188f945c69c9ea" }, "src/L1/L1ERC721Bridge.sol": { - "initCodeHash": "0x63dc4da75200f4b968f57e27e81834e6eb3f6625826410882526ab1eec7847ff", - "sourceCodeHash": "0xfec29cfbb7aa05473e32a6c2484deebfc1ff50c0e08c42e8ee70696ad701ceaa" + "initCodeHash": "0xb3bf093ea83a24574a6093bebf5b2aea707355ed8d6702b2b5eb292e75b6ae42", + "sourceCodeHash": "0x289de9f40898b6305deecc6b60cdf566aa6c6a1444f713c3a0af23ea7878207e" }, "src/L1/L1StandardBridge.sol": { "initCodeHash": "0x2868b09ecbe9f2bbc885605c2886b4c79f1c8e4171626c63776603b1b84698a8", @@ -88,8 +88,8 @@ "sourceCodeHash": "0x56edf0f36366326a92722ae3c7502bce3d80b2ee5e354181dc09ba801437a488" }, "src/L2/L2ERC721Bridge.sol": { - "initCodeHash": "0x558fff5939a26b9d5d86e6b907d9dd9c7c917eaef7657a8b5acfeb58de1442f0", - "sourceCodeHash": "0xca9acd19fd5f42e6a7a5b1de6359f2d841814fb54d377664c2fe9c3f9c6be34a" + "initCodeHash": "0xb8236514beabcc1830e5e7e9eccb965e30d01f8bbb5a2d8b4f3d241c2d12819b", + "sourceCodeHash": "0x268e34289b7b8a5d1785af1907bd07d1c8ce4a9e3ea1685bdd9a77904dab3557" }, "src/L2/L2StandardBridge.sol": { "initCodeHash": "0x651eed10044d0b19b7e4eba864345df15e252be1401f39a552ec0d2f9c4df064", diff --git a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol index dd01ff5cd2d3..bd9b31c1e589 100644 --- a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol @@ -28,8 +28,8 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver { ISuperchainConfig public superchainConfig; /// @notice Semantic version. - /// @custom:semver 2.1.1-beta.4 - string public constant version = "2.1.1-beta.4"; + /// @custom:semver 2.2.0-beta.1 + string public constant version = "2.2.0-beta.1"; /// @notice Constructs the L1ERC721Bridge contract. constructor() ERC721Bridge() { @@ -107,8 +107,8 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver { require(_remoteToken != address(0), "L1ERC721Bridge: remote token cannot be address(0)"); // Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId) - bytes memory message = abi.encodeWithSelector( - IL2ERC721Bridge.finalizeBridgeERC721.selector, _remoteToken, _localToken, _from, _to, _tokenId, _extraData + bytes memory message = abi.encodeCall( + IL2ERC721Bridge.finalizeBridgeERC721, (_remoteToken, _localToken, _from, _to, _tokenId, _extraData) ); // Lock token into bridge diff --git a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol index 950f25203981..7db9cc17aaa7 100644 --- a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol @@ -26,8 +26,8 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// wait for the one-week challenge period to elapse before their Optimism-native NFT /// can be refunded on L2. contract L2ERC721Bridge is ERC721Bridge, ISemver { - /// @custom:semver 1.7.1-beta.3 - string public constant version = "1.7.1-beta.3"; + /// @custom:semver 1.8.0-beta.1 + string public constant version = "1.8.0-beta.1"; /// @notice Constructs the L2ERC721Bridge contract. constructor() ERC721Bridge() { @@ -117,8 +117,8 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver { // slither-disable-next-line reentrancy-events IOptimismMintableERC721(_localToken).burn(_from, _tokenId); - bytes memory message = abi.encodeWithSelector( - IL1ERC721Bridge.finalizeBridgeERC721.selector, remoteToken, _localToken, _from, _to, _tokenId, _extraData + bytes memory message = abi.encodeCall( + IL1ERC721Bridge.finalizeBridgeERC721, (remoteToken, _localToken, _from, _to, _tokenId, _extraData) ); // Send message to L1 bridge diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 81b128f229b2..96238b41b367 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -118,3 +118,17 @@ rules: exclude: - packages/contracts-bedrock/test - packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol + + - id: sol-style-use-abi-encodecall + languages: [solidity] + severity: ERROR + message: Use abi.encodeCall instead of abi.encodeWithSelector + patterns: + - pattern: | + abi.encodeWithSelector(...); + paths: + exclude: + - packages/contracts-bedrock/test + - packages/contracts-bedrock/src/L1/OPContractsManager.sol + - packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol + - packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol From 38db5117cd4cc4ff97be0701049f8dd98bd56cb8 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 25 Oct 2024 03:29:13 +0100 Subject: [PATCH 029/451] update test folder so segrep require and revert checks pass (#12628) * update test folder so that smegrep require and revert checks pass for it * fix test --- packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol | 4 ++-- packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol | 4 ++-- .../contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol | 5 ++++- .../contracts-bedrock/test/dispute/FaultDisputeGame.t.sol | 2 +- .../contracts-bedrock/test/invariants/FaultDisputeGame.t.sol | 2 +- .../OptimismSuperchainERC20/handlers/Protocol.t.sol | 4 +++- .../helpers/MockL2ToL2CrossDomainMessenger.t.sol | 2 +- packages/contracts-bedrock/test/mocks/Callers.sol | 2 +- .../test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol | 2 +- packages/contracts-bedrock/test/universal/Proxy.t.sol | 4 ++-- semgrep/sol-rules.yaml | 2 -- 11 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index 0f2c51b4bf3b..50e1ac91c977 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -80,7 +80,7 @@ contract SystemConfigInterop_Test is CommonTest { /// @dev Tests that adding a dependency as not the dependency manager reverts. function testFuzz_addDependency_notDependencyManager_reverts(uint256 _chainId) public { - require(alice != _systemConfigInterop().dependencyManager(), "SystemConfigInterop_Test-100"); + require(alice != _systemConfigInterop().dependencyManager(), "SystemConfigInterop_Test: 100"); vm.expectRevert("SystemConfig: caller is not the dependency manager"); vm.prank(alice); _systemConfigInterop().addDependency(_chainId); @@ -102,7 +102,7 @@ contract SystemConfigInterop_Test is CommonTest { /// @dev Tests that removing a dependency as not the dependency manager reverts. function testFuzz_removeDependency_notDependencyManager_reverts(uint256 _chainId) public { - require(alice != _systemConfigInterop().dependencyManager(), "SystemConfigInterop_Test-100"); + require(alice != _systemConfigInterop().dependencyManager(), "SystemConfigInterop_Test: 100"); vm.expectRevert("SystemConfig: caller is not the dependency manager"); vm.prank(alice); _systemConfigInterop().removeDependency(_chainId); diff --git a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol index f81d96b040f4..ed4d598f4392 100644 --- a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol +++ b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol @@ -131,7 +131,7 @@ contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { // Execute the function call vm.prank(depositor); (bool success,) = address(l1Block).call(calldataPacked); - require(success, "Function call failed"); + require(success, "GasPriceOracleEcotone_Test: Function call failed"); } /// @dev Tests that `setEcotone` is only callable by the depositor. @@ -222,7 +222,7 @@ contract GasPriceOracleFjordActive_Test is GasPriceOracle_Test { vm.prank(depositor); (bool success,) = address(l1Block).call(calldataPacked); - require(success, "Function call failed"); + require(success, "GasPriceOracleFjordActive_Test: Function call failed"); } /// @dev Tests that `setFjord` cannot be called when Fjord is already activate diff --git a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol index b5dbac76f6c6..c3cdf2c58d44 100644 --- a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol +++ b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol @@ -138,7 +138,10 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In function test_setAnchorState_invalidGame_fails() public { // Confirm that the anchor state is older than the game state. (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); - require(l2BlockNumber < gameProxy.l2BlockNumber(), "l2BlockNumber < gameProxy.l2BlockNumber()"); + require( + l2BlockNumber < gameProxy.l2BlockNumber(), + "AnchorStateRegistry_TryUpdateAnchorState_Test: l2BlockNumber < gameProxy.l2BlockNumber()" + ); // Mock the state that we want. vm.mockCall( diff --git a/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol b/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol index 8bb25273972a..00f3a8b19f9c 100644 --- a/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol +++ b/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol @@ -2641,7 +2641,7 @@ contract FaultDispute_1v1_Actors_Test is FaultDisputeGame_Init { (uint256 numMovesA,) = dishonest.move(); (uint256 numMovesB, bool success) = honest.move(); - require(success, "Honest actor's moves should always be successful"); + require(success, "FaultDispute_1v1_Actors_Test: Honest actor's moves should always be successful"); // If both actors have run out of moves, we're done. if (numMovesA == 0 && numMovesB == 0) break; diff --git a/packages/contracts-bedrock/test/invariants/FaultDisputeGame.t.sol b/packages/contracts-bedrock/test/invariants/FaultDisputeGame.t.sol index 49bf2a106bbf..94b46930ad49 100644 --- a/packages/contracts-bedrock/test/invariants/FaultDisputeGame.t.sol +++ b/packages/contracts-bedrock/test/invariants/FaultDisputeGame.t.sol @@ -69,7 +69,7 @@ contract FaultDisputeGame_Solvency_Invariant is FaultDisputeGame_Init { assertEq(DEFAULT_SENDER.balance, type(uint96).max - rootBond); assertEq(address(actor).balance, actor.totalBonded() + rootBond); } else { - revert("unreachable"); + revert("FaultDisputeGame_Solvency_Invariant: unreachable"); } assertEq(address(gameProxy).balance, 0); diff --git a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol index 770be029bbbe..490a38bec1cf 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/handlers/Protocol.t.sol @@ -157,7 +157,9 @@ contract ProtocolHandler is TestBase, StdUtils, Actors { bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); // Foundry invariant erroneously show other unrelated invariant breaking // when this deployment fails due to a create2 collision, so we revert eagerly instead - require(MESSENGER.superTokenAddresses(chainId, realSalt) == address(0), "skip duplicate deployment"); + require( + MESSENGER.superTokenAddresses(chainId, realSalt) == address(0), "ProtocolHandler: skip duplicate deployment" + ); // what we use in the tests to walk around two contracts needing two different addresses // tbf we could be using CREATE1, but this feels more verbose diff --git a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol index 2d4a57c46c3c..94f275e26551 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismSuperchainERC20/helpers/MockL2ToL2CrossDomainMessenger.t.sol @@ -89,7 +89,7 @@ contract MockL2ToL2CrossDomainMessenger { function sendMessage(uint256 chainId, address, /*recipient*/ bytes calldata data) external { address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; if (crossChainRecipient == msg.sender) { - require(false, "same chain"); + require(false, "MockL2ToL2CrossDomainMessenger: same chain"); } (address recipient, uint256 amount) = _decodePayload(data); diff --git a/packages/contracts-bedrock/test/mocks/Callers.sol b/packages/contracts-bedrock/test/mocks/Callers.sol index aa7a2dc8b228..b8dd70df15e1 100644 --- a/packages/contracts-bedrock/test/mocks/Callers.sol +++ b/packages/contracts-bedrock/test/mocks/Callers.sol @@ -84,7 +84,7 @@ contract ConfigurableCaller { /// @dev Any call will revert contract Reverter { function doRevert() public pure { - revert("Reverter reverted"); + revert("Reverter: Reverter reverted"); } fallback() external { diff --git a/packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol b/packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol index ac0c264d3038..89536e2e4493 100644 --- a/packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol +++ b/packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol @@ -93,7 +93,7 @@ contract CompatibilityFallbackHandler is DefaultCallbackHandler, ISignatureValid GnosisSafe safe = GnosisSafe(payable(msg.sender)); bytes32 messageHash = getMessageHashForSafe(safe, _data); if (_signature.length == 0) { - require(safe.signedMessages(messageHash) != 0, "Hash not approved"); + require(safe.signedMessages(messageHash) != 0, "CompatibilityFallbackHandler: Hash not approved"); } else { safe.checkSignatures(messageHash, _data, _signature); } diff --git a/packages/contracts-bedrock/test/universal/Proxy.t.sol b/packages/contracts-bedrock/test/universal/Proxy.t.sol index 66c3f1d24268..90d70d261775 100644 --- a/packages/contracts-bedrock/test/universal/Proxy.t.sol +++ b/packages/contracts-bedrock/test/universal/Proxy.t.sol @@ -19,7 +19,7 @@ contract SimpleStorage { contract Clasher { function upgradeTo(address) external pure { - revert("upgradeTo"); + revert("Clasher: upgradeTo"); } } @@ -228,7 +228,7 @@ contract Proxy_Test is Test { // not as the owner so that the call passes through. // The implementation will revert so we can be // sure that the call passed through. - vm.expectRevert(bytes("upgradeTo")); + vm.expectRevert(bytes("Clasher: upgradeTo")); proxy.upgradeTo(address(0)); { diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 96238b41b367..08cc5f74685d 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -94,7 +94,6 @@ rules: - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: exclude: - - packages/contracts-bedrock/test - packages/contracts-bedrock/src/libraries/Bytes.sol - packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol - packages/contracts-bedrock/src/cannon/MIPS.sol @@ -116,7 +115,6 @@ rules: - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: exclude: - - packages/contracts-bedrock/test - packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol - id: sol-style-use-abi-encodecall From 9f22034fe49f301532f362b5d2dd02017bb4556a Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Fri, 25 Oct 2024 15:45:36 +0800 Subject: [PATCH 030/451] do forge clean when make clean (#12635) --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 641e4b11699e..791eba38d453 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,7 @@ mod-tidy: ## Cleans up unused dependencies in Go modules clean: ## Removes all generated files under bin/ rm -rf ./bin + cd packages/contracts-bedrock/ && forge clean .PHONY: clean nuke: clean devnet-clean ## Completely clean the project directory From da4c33c50e6ecbada5704477132fc35a9e8ad15b Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 25 Oct 2024 20:47:33 +0700 Subject: [PATCH 031/451] op-supervisor: Cross-safe updates [rebased] (#12624) * op-supervisor: cross-safe-updates PR squashed op-supervisor: experimental cross-safety, with hazard detection tweak: Add some errors/error returns in backend/cross. wip: Chain index <> ID mapping. fix: Check parent instead of re-checking hazardBlock. Remove Hazard Work Write missing DB Bindings OpenBlock, LocallyDerivedFrom, CrossDerivedFrom Configurable WorkFn for Workers op-supervisor: move chain-index <> chain ID translation into dependency set, fix some interfaces op-supervisor: update cross-safety worker routine op-supervisor: update more error handling op-supervisor: move errors to types package op-supervisor: check CanExecuteAt and CanInitiateAt op-supervisor: determine cross-safe candidate and L1 scope, and more fixes todo L1 scope increment op-supervisor: cross-safe L1 scope bump op-supervisor: dependency set getter op-supervisor: L1 scope increment fix op-supervisor: fix cross-safe updates typing op-node: signal L1 traversal of derivation to supervisor op-supervisor: fromda fixes and tests op-supervisor: fix OpenBlock, fix/add missing interface methods, hook up cross-safe worker routines OpenBlock to return map[uint32]ExecutingMessage Add Frontier Unit Tests fix WithParent panic op-node: register L1 traversal with op-supervisor op-node,op-supervisor: add logging, temp work around for interop local-safe updates Add safe_start_test, unsafe_start_test Add safe_update_test and unsafe_update_test add worker_test op-supervisor: fix cross-safe L1 scope bumping op-supervisor: fix logs DB test Co-authored-by: axelKingsley Co-authored-by: Tyler Smith * op-node: fix interop deriver test * op-e2e: fix interop action test * op-supervisor: improve map init * op-node: link interop TODO comment to issue, in engine events emitter * op-supervisor: cleanup Worker instances of tests --------- Co-authored-by: axelKingsley Co-authored-by: Tyler Smith --- op-e2e/actions/interop/interop_test.go | 22 +- op-node/rollup/derive/deriver.go | 3 +- op-node/rollup/engine/events.go | 14 + op-node/rollup/interop/interop.go | 14 +- op-node/rollup/interop/interop_test.go | 2 +- op-supervisor/supervisor/backend/backend.go | 55 ++- .../supervisor/backend/cross/safe_frontier.go | 59 +++ .../backend/cross/safe_frontier_test.go | 201 +++++++++ .../supervisor/backend/cross/safe_start.go | 104 +++++ .../backend/cross/safe_start_test.go | 341 +++++++++++++++ .../supervisor/backend/cross/safe_update.go | 116 +++++ .../backend/cross/safe_update_test.go | 399 ++++++++++++++++++ .../backend/cross/unsafe_frontier.go | 63 +++ .../backend/cross/unsafe_frontier_test.go | 130 ++++++ .../supervisor/backend/cross/unsafe_start.go | 100 +++++ .../backend/cross/unsafe_start_test.go | 280 ++++++++++++ .../supervisor/backend/cross/unsafe_update.go | 79 ++++ .../backend/cross/unsafe_update_test.go | 212 ++++++++++ .../supervisor/backend/cross/worker.go | 103 +++++ .../supervisor/backend/cross/worker_test.go | 106 +++++ .../backend/processors/chain_processor.go | 6 +- .../backend/processors/log_processor_test.go | 2 +- 22 files changed, 2390 insertions(+), 21 deletions(-) create mode 100644 op-supervisor/supervisor/backend/cross/safe_frontier.go create mode 100644 op-supervisor/supervisor/backend/cross/safe_frontier_test.go create mode 100644 op-supervisor/supervisor/backend/cross/safe_start.go create mode 100644 op-supervisor/supervisor/backend/cross/safe_start_test.go create mode 100644 op-supervisor/supervisor/backend/cross/safe_update.go create mode 100644 op-supervisor/supervisor/backend/cross/safe_update_test.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_frontier.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_frontier_test.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_start.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_start_test.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_update.go create mode 100644 op-supervisor/supervisor/backend/cross/unsafe_update_test.go create mode 100644 op-supervisor/supervisor/backend/cross/worker.go create mode 100644 op-supervisor/supervisor/backend/cross/worker_test.go diff --git a/op-e2e/actions/interop/interop_test.go b/op-e2e/actions/interop/interop_test.go index c345404907b0..b04a07da4826 100644 --- a/op-e2e/actions/interop/interop_test.go +++ b/op-e2e/actions/interop/interop_test.go @@ -41,6 +41,18 @@ func TestInteropVerifier(gt *testing.T) { l1Miner.L1Client(t, sd.RollupCfg), l1Miner.BlobStore(), &sync.Config{}, helpers.WithInteropBackend(verMockBackend)) + // Genesis block will be registered as local-safe when we first trigger the derivation pipeline + seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) + require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) + return nil + } + verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) + require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) + return nil + } + seq.ActL2PipelineFull(t) ver.ActL2PipelineFull(t) @@ -93,8 +105,10 @@ func TestInteropVerifier(gt *testing.T) { // Sync the L1 block, to verify the L2 block as local-safe. seqMockBackend.UpdateLocalUnsafeFn = nil + nextL2 := uint64(0) seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { - require.Equal(t, uint64(1), lastDerived.Number) + require.Equal(t, nextL2, lastDerived.Number) + nextL2 += 1 return nil } seqMockBackend.SafeViewFn = func(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) { @@ -106,6 +120,7 @@ func TestInteropVerifier(gt *testing.T) { seq.ActL1HeadSignal(t) l1Head := seq.SyncStatus().HeadL1 seq.ActL2PipelineFull(t) + require.Equal(t, uint64(2), nextL2) status = seq.SyncStatus() require.Equal(t, uint64(1), status.UnsafeL2.Number) @@ -143,8 +158,10 @@ func TestInteropVerifier(gt *testing.T) { require.Equal(t, uint64(1), head.Number) return nil } + nextL2 = 0 verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { - require.Equal(t, uint64(1), lastDerived.Number) + require.Equal(t, nextL2, lastDerived.Number) + nextL2 += 1 require.Equal(t, l1Head.ID(), derivedFrom.ID()) return nil } @@ -163,6 +180,7 @@ func TestInteropVerifier(gt *testing.T) { } ver.ActL1HeadSignal(t) ver.ActL2PipelineFull(t) + require.Equal(t, uint64(2), nextL2) status = ver.SyncStatus() require.Equal(t, uint64(1), status.UnsafeL2.Number, "synced the block") require.Equal(t, uint64(0), status.CrossUnsafeL2.Number, "not cross-verified yet") diff --git a/op-node/rollup/derive/deriver.go b/op-node/rollup/derive/deriver.go index 760891648524..4a897f4ee4ec 100644 --- a/op-node/rollup/derive/deriver.go +++ b/op-node/rollup/derive/deriver.go @@ -20,6 +20,7 @@ func (d DeriverIdleEvent) String() string { type DeriverL1StatusEvent struct { Origin eth.L1BlockRef + LastL2 eth.L2BlockRef } func (d DeriverL1StatusEvent) String() string { @@ -99,7 +100,7 @@ func (d *PipelineDeriver) OnEvent(ev event.Event) bool { attrib, err := d.pipeline.Step(d.ctx, x.PendingSafe) postOrigin := d.pipeline.Origin() if preOrigin != postOrigin { - d.emitter.Emit(DeriverL1StatusEvent{Origin: postOrigin}) + d.emitter.Emit(DeriverL1StatusEvent{Origin: postOrigin, LastL2: x.PendingSafe}) } if err == io.EOF { d.pipeline.log.Debug("Derivation process went idle", "progress", d.pipeline.Origin(), "err", err) diff --git a/op-node/rollup/engine/events.go b/op-node/rollup/engine/events.go index b05410a6dcef..73d0a77abb52 100644 --- a/op-node/rollup/engine/events.go +++ b/op-node/rollup/engine/events.go @@ -98,6 +98,15 @@ func (ev PendingSafeUpdateEvent) String() string { return "pending-safe-update" } +type InteropPendingSafeChangedEvent struct { + Ref eth.L2BlockRef + DerivedFrom eth.L1BlockRef +} + +func (ev InteropPendingSafeChangedEvent) String() string { + return "interop-pending-safe-changed" +} + // PromotePendingSafeEvent signals that a block can be marked as pending-safe, and/or safe. type PromotePendingSafeEvent struct { Ref eth.L2BlockRef @@ -407,6 +416,11 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { DerivedFrom: x.DerivedFrom, }) } + // TODO(#12646): temporary interop work-around, assumes Holocene local-safe progression behavior. + d.emitter.Emit(InteropPendingSafeChangedEvent{ + Ref: x.Ref, + DerivedFrom: x.DerivedFrom, + }) case PromoteLocalSafeEvent: d.ec.SetLocalSafeHead(x.Ref) d.emitter.Emit(LocalSafeUpdateEvent(x)) diff --git a/op-node/rollup/interop/interop.go b/op-node/rollup/interop/interop.go index 904f93d94112..2421fcd31377 100644 --- a/op-node/rollup/interop/interop.go +++ b/op-node/rollup/interop/interop.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-node/rollup/event" "github.com/ethereum-optimism/optimism/op-node/rollup/finality" @@ -83,10 +84,17 @@ func (d *InteropDeriver) OnEvent(ev event.Event) bool { switch x := ev.(type) { case engine.UnsafeUpdateEvent: d.onLocalUnsafeUpdate(x) - case engine.LocalSafeUpdateEvent: - d.onLocalSafeUpdate(x) + case engine.InteropPendingSafeChangedEvent: + d.onInteropPendingSafeChangedEvent(x) case finality.FinalizeL1Event: d.onFinalizedL1(x) + case derive.DeriverL1StatusEvent: + d.log.Debug("deriver L1 traversal event", "l1", x.Origin, "l2", x.LastL2) + // Register traversal of L1, repeat the last local-safe L2 + d.onInteropPendingSafeChangedEvent(engine.InteropPendingSafeChangedEvent{ + Ref: x.LastL2, + DerivedFrom: x.Origin, + }) case engine.CrossUnsafeUpdateEvent: if err := d.onCrossUnsafe(x); err != nil { d.log.Error("Failed to process cross-unsafe update", "err", err) @@ -117,7 +125,7 @@ func (d *InteropDeriver) onLocalUnsafeUpdate(x engine.UnsafeUpdateEvent) { d.emitter.Emit(engine.RequestCrossUnsafeEvent{}) } -func (d *InteropDeriver) onLocalSafeUpdate(x engine.LocalSafeUpdateEvent) { +func (d *InteropDeriver) onInteropPendingSafeChangedEvent(x engine.InteropPendingSafeChangedEvent) { d.log.Debug("Signaling derived-from update to interop backend", "derivedFrom", x.DerivedFrom, "block", x.Ref) ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout) defer cancel() diff --git a/op-node/rollup/interop/interop_test.go b/op-node/rollup/interop/interop_test.go index 4078e4881a07..3dc0e7708d04 100644 --- a/op-node/rollup/interop/interop_test.go +++ b/op-node/rollup/interop/interop_test.go @@ -93,7 +93,7 @@ func TestInteropDeriver(t *testing.T) { derivedFrom := testutils.RandomBlockRef(rng) localSafe := testutils.RandomL2BlockRef(rng) interopBackend.ExpectUpdateLocalSafe(chainID, derivedFrom, localSafe, nil) - interopDeriver.OnEvent(engine.LocalSafeUpdateEvent{ + interopDeriver.OnEvent(engine.InteropPendingSafeChangedEvent{ Ref: localSafe, DerivedFrom: derivedFrom, }) diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 506bcefd67c0..898157558931 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-supervisor/config" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/cross" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors" @@ -43,6 +44,12 @@ type SupervisorBackend struct { // chainProcessors are notified of new unsafe blocks, and add the unsafe log events data into the events DB chainProcessors map[types.ChainID]*processors.ChainProcessor + // crossSafeProcessors take local-safe data and promote it to cross-safe when verified + crossSafeProcessors map[types.ChainID]*cross.Worker + + // crossUnsafeProcessors take local-unsafe data and promote it to cross-unsafe when verified + crossUnsafeProcessors map[types.ChainID]*cross.Worker + // synchronousProcessors disables background-workers, // requiring manual triggers for the backend to process anything. synchronousProcessors bool @@ -71,18 +78,18 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg // create initial per-chain resources chainsDBs := db.NewChainsDB(logger, depSet) - chainProcessors := make(map[types.ChainID]*processors.ChainProcessor, len(chains)) - chainMetrics := make(map[types.ChainID]*chainMetrics, len(chains)) // create the supervisor backend super := &SupervisorBackend{ - logger: logger, - m: m, - dataDir: cfg.Datadir, - depSet: depSet, - chainDBs: chainsDBs, - chainProcessors: chainProcessors, - chainMetrics: chainMetrics, + logger: logger, + m: m, + dataDir: cfg.Datadir, + depSet: depSet, + chainDBs: chainsDBs, + chainProcessors: make(map[types.ChainID]*processors.ChainProcessor, len(chains)), + chainMetrics: make(map[types.ChainID]*chainMetrics, len(chains)), + crossUnsafeProcessors: make(map[types.ChainID]*cross.Worker, len(chains)), + crossSafeProcessors: make(map[types.ChainID]*cross.Worker, len(chains)), // For testing we can avoid running the processors. synchronousProcessors: cfg.SynchronousProcessors, } @@ -117,6 +124,17 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf su.chainProcessors[chainID] = chainProcessor } + // initialize all cross-unsafe processors + for _, chainID := range chains { + worker := cross.NewCrossUnsafeWorker(su.logger, chainID, su.chainDBs) + su.crossUnsafeProcessors[chainID] = worker + } + // initialize all cross-safe processors + for _, chainID := range chains { + worker := cross.NewCrossSafeWorker(su.logger, chainID, su.chainDBs) + su.crossSafeProcessors[chainID] = worker + } + // the config has some RPC connections to attach to the chain-processors for _, rpc := range cfg.L2RPCs { err := su.attachRPC(ctx, rpc) @@ -231,6 +249,12 @@ func (su *SupervisorBackend) Start(ctx context.Context) error { for _, processor := range su.chainProcessors { processor.StartBackground() } + for _, worker := range su.crossUnsafeProcessors { + worker.StartBackground() + } + for _, worker := range su.crossSafeProcessors { + worker.StartBackground() + } } return nil @@ -249,6 +273,19 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error { processor.Close() } clear(su.chainProcessors) + + for id, worker := range su.crossUnsafeProcessors { + su.logger.Info("stopping cross-unsafe processor", "chainID", id) + worker.Close() + } + clear(su.crossUnsafeProcessors) + + for id, worker := range su.crossSafeProcessors { + su.logger.Info("stopping cross-safe processor", "chainID", id) + worker.Close() + } + clear(su.crossSafeProcessors) + // close the databases return su.chainDBs.Close() } diff --git a/op-supervisor/supervisor/backend/cross/safe_frontier.go b/op-supervisor/supervisor/backend/cross/safe_frontier.go new file mode 100644 index 000000000000..31b48843fd9a --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_frontier.go @@ -0,0 +1,59 @@ +package cross + +import ( + "errors" + "fmt" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type SafeFrontierCheckDeps interface { + CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) + + CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) + + DependencySet() depset.DependencySet +} + +// HazardSafeFrontierChecks verifies all the hazard blocks are either: +// - already cross-safe. +// - the first (if not first: local blocks to verify before proceeding) +// local-safe block, after the cross-safe block. +func HazardSafeFrontierChecks(d SafeFrontierCheckDeps, inL1DerivedFrom eth.BlockID, hazards map[types.ChainIndex]types.BlockSeal) error { + depSet := d.DependencySet() + for hazardChainIndex, hazardBlock := range hazards { + hazardChainID, err := depSet.ChainIDFromIndex(hazardChainIndex) + if err != nil { + if errors.Is(err, types.ErrUnknownChain) { + err = fmt.Errorf("cannot cross-safe verify block %s of unknown chain index %s: %w", hazardBlock, hazardChainIndex, types.ErrConflict) + } + return err + } + initDerivedFrom, err := d.CrossDerivedFrom(hazardChainID, hazardBlock.ID()) + if err != nil { + if errors.Is(err, types.ErrFuture) { + // If not in cross-safe scope, then check if it's the candidate cross-safe block. + initDerivedFrom, initSelf, err := d.CandidateCrossSafe(hazardChainID) + if err != nil { + return fmt.Errorf("failed to determine cross-safe candidate block of hazard dependency %s (chain %s): %w", hazardBlock, hazardChainID, err) + } + if initSelf.Number == hazardBlock.Number && initSelf.ID() != hazardBlock.ID() { + return fmt.Errorf("expected block %s (chain %d) does not match candidate local-safe block %s: %w", + hazardBlock, hazardChainID, initSelf, types.ErrConflict) + } + if initDerivedFrom.Number > inL1DerivedFrom.Number { + return fmt.Errorf("local-safe hazard block %s derived from L1 block %s is after scope %s: %w", + hazardBlock.ID(), initDerivedFrom, inL1DerivedFrom, types.ErrOutOfScope) + } + } else { + return fmt.Errorf("failed to determine cross-derived of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err) + } + } else if initDerivedFrom.Number > inL1DerivedFrom.Number { + return fmt.Errorf("cross-safe hazard block %s derived from L1 block %s is after scope %s: %w", + hazardBlock.ID(), initDerivedFrom, inL1DerivedFrom, types.ErrOutOfScope) + } + } + return nil +} diff --git a/op-supervisor/supervisor/backend/cross/safe_frontier_test.go b/op-supervisor/supervisor/backend/cross/safe_frontier_test.go new file mode 100644 index 000000000000..6848acf841cd --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_frontier_test.go @@ -0,0 +1,201 @@ +package cross + +import ( + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestHazardSafeFrontierChecks(t *testing.T) { + t.Run("empty hazards", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + l1DerivedFrom := eth.BlockID{} + hazards := map[types.ChainIndex]types.BlockSeal{} + // when there are no hazards, + // no work is done, and no error is returned + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.NoError(t, err) + }) + t.Run("unknown chain", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{ + deps: mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, types.ErrUnknownChain + }, + }, + } + l1DerivedFrom := eth.BlockID{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + // when there is one hazard, and ChainIDFromIndex returns ErrUnknownChain, + // an error is returned as a ErrConflict + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorIs(t, err, types.ErrConflict) + }) + t.Run("initDerivedFrom in scope", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{Number: 1}, nil + } + l1DerivedFrom := eth.BlockID{Number: 2} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + // when there is one hazard, and CrossDerivedFrom returns a BlockSeal within scope + // (ie the hazard's block number is less than or equal to the derivedFrom block number), + // no error is returned + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.NoError(t, err) + }) + t.Run("initDerivedFrom out of scope", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{Number: 3}, nil + } + l1DerivedFrom := eth.BlockID{Number: 2} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + // when there is one hazard, and CrossDerivedFrom returns a BlockSeal out of scope + // (ie the hazard's block number is greater than the derivedFrom block number), + // an error is returned as a ErrOutOfScope + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorIs(t, err, types.ErrOutOfScope) + }) + t.Run("errFuture: candidate cross safe failure", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{Number: 3}, types.ErrFuture + } + sfcd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, + eth.BlockRef{Number: 3, Hash: common.BytesToHash([]byte{0x01})}, + errors.New("some error") + } + l1DerivedFrom := eth.BlockID{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + // when there is one hazard, and CrossDerivedFrom returns an ErrFuture, + // and CandidateCrossSafe returns an error, + // the error from CandidateCrossSafe is returned + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorContains(t, err, "some error") + }) + t.Run("errFuture: expected block does not match candidate", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{}, types.ErrFuture + } + sfcd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, + eth.BlockRef{Number: 3, Hash: common.BytesToHash([]byte{0x01})}, + nil + } + l1DerivedFrom := eth.BlockID{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3, Hash: common.BytesToHash([]byte{0x02})}} + // when there is one hazard, and CrossDerivedFrom returns an ErrFuture, + // and CandidateCrossSafe returns a candidate that does not match the hazard, + // (ie the candidate's block number is the same as the hazard's block number, but the hashes are different), + // an error is returned as a ErrConflict + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorIs(t, err, types.ErrConflict) + }) + t.Run("errFuture: local-safe hazard out of scope", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{}, types.ErrFuture + } + sfcd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{Number: 9}, + eth.BlockRef{}, + nil + } + l1DerivedFrom := eth.BlockID{Number: 8} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3, Hash: common.BytesToHash([]byte{0x02})}} + // when there is one hazard, and CrossDerivedFrom returns an ErrFuture, + // and the initDerivedFrom is out of scope, + // an error is returned as a ErrOutOfScope + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorIs(t, err, types.ErrOutOfScope) + }) + t.Run("CrossDerivedFrom Error", func(t *testing.T) { + sfcd := &mockSafeFrontierCheckDeps{} + sfcd.crossDerivedFromFn = func() (types.BlockSeal, error) { + return types.BlockSeal{}, errors.New("some error") + } + sfcd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{Number: 9}, + eth.BlockRef{}, + nil + } + l1DerivedFrom := eth.BlockID{Number: 8} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3, Hash: common.BytesToHash([]byte{0x02})}} + // when there is one hazard, and CrossDerivedFrom returns an ErrFuture, + // and the initDerivedFrom is out of scope, + // an error is returned as a ErrOutOfScope + err := HazardSafeFrontierChecks(sfcd, l1DerivedFrom, hazards) + require.ErrorContains(t, err, "some error") + }) +} + +type mockSafeFrontierCheckDeps struct { + deps mockDependencySet + candidateCrossSafeFn func() (derivedFromScope, crossSafe eth.BlockRef, err error) + crossDerivedFromFn func() (derivedFrom types.BlockSeal, err error) +} + +func (m *mockSafeFrontierCheckDeps) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) { + if m.candidateCrossSafeFn != nil { + return m.candidateCrossSafeFn() + } + return eth.BlockRef{}, eth.BlockRef{}, nil +} + +func (m *mockSafeFrontierCheckDeps) CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { + if m.crossDerivedFromFn != nil { + return m.crossDerivedFromFn() + } + return types.BlockSeal{}, nil +} + +func (m *mockSafeFrontierCheckDeps) DependencySet() depset.DependencySet { + return m.deps +} + +type mockDependencySet struct { + chainIDFromIndexfn func() (types.ChainID, error) + canExecuteAtfn func() (bool, error) + canInitiateAtfn func() (bool, error) +} + +func (m mockDependencySet) CanExecuteAt(chain types.ChainID, timestamp uint64) (bool, error) { + if m.canExecuteAtfn != nil { + return m.canExecuteAtfn() + } + return true, nil +} + +func (m mockDependencySet) CanInitiateAt(chain types.ChainID, timestamp uint64) (bool, error) { + if m.canInitiateAtfn != nil { + return m.canInitiateAtfn() + } + return true, nil +} + +func (m mockDependencySet) ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error) { + if m.chainIDFromIndexfn != nil { + return m.chainIDFromIndexfn() + } + return types.ChainID{}, nil +} + +func (m mockDependencySet) ChainIndexFromID(chain types.ChainID) (types.ChainIndex, error) { + return types.ChainIndex(0), nil +} + +func (m mockDependencySet) Chains() []types.ChainID { + return nil +} + +func (m mockDependencySet) HasChain(chain types.ChainID) bool { + return true +} diff --git a/op-supervisor/supervisor/backend/cross/safe_start.go b/op-supervisor/supervisor/backend/cross/safe_start.go new file mode 100644 index 000000000000..acd97304ed14 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_start.go @@ -0,0 +1,104 @@ +package cross + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type SafeStartDeps interface { + Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) + + CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) + + DependencySet() depset.DependencySet +} + +// CrossSafeHazards checks if the given messages all exist and pass invariants. +// It returns a hazard-set: if any intra-block messaging happened, +// these hazard blocks have to be verified. +func CrossSafeHazards(d SafeStartDeps, chainID types.ChainID, inL1DerivedFrom eth.BlockID, + candidate types.BlockSeal, execMsgs []*types.ExecutingMessage) (hazards map[types.ChainIndex]types.BlockSeal, err error) { + + hazards = make(map[types.ChainIndex]types.BlockSeal) + + // Warning for future: If we have sub-second distinct blocks (different block number), + // we need to increase precision on the above timestamp invariant. + // Otherwise a local block can depend on a future local block of the same chain, + // simply by pulling in a block of another chain, + // which then depends on a block of the original chain, + // all with the same timestamp, without message cycles. + + depSet := d.DependencySet() + + if len(execMsgs) > 0 { + if ok, err := depSet.CanExecuteAt(chainID, candidate.Timestamp); err != nil { + return nil, fmt.Errorf("cannot check message execution of block %s (chain %s): %w", candidate, chainID, err) + } else if !ok { + return nil, fmt.Errorf("cannot execute messages in block %s (chain %s): %w", candidate, chainID, types.ErrConflict) + } + } + + // check all executing messages + for _, msg := range execMsgs { + initChainID, err := depSet.ChainIDFromIndex(msg.Chain) + if err != nil { + if errors.Is(err, types.ErrUnknownChain) { + err = fmt.Errorf("msg %s may not execute from unknown chain %s: %w", msg, msg.Chain, types.ErrConflict) + } + return nil, err + } + if ok, err := depSet.CanInitiateAt(initChainID, msg.Timestamp); err != nil { + return nil, fmt.Errorf("cannot check message initiation of msg %s (chain %s): %w", msg, chainID, err) + } else if !ok { + return nil, fmt.Errorf("cannot allow initiating message %s (chain %s): %w", msg, chainID, types.ErrConflict) + } + if msg.Timestamp < candidate.Timestamp { + // If timestamp is older: invariant ensures non-cyclic ordering relative to other messages. + // Check that the block that they are included in is cross-safe already. + includedIn, err := d.Check(initChainID, msg.BlockNum, msg.LogIdx, msg.Hash) + if err != nil { + return nil, fmt.Errorf("executing msg %s failed check: %w", msg, err) + } + initDerivedFrom, err := d.CrossDerivedFrom(initChainID, includedIn.ID()) + if err != nil { + return nil, fmt.Errorf("msg %s included in non-cross-safe block %s: %w", msg, includedIn, err) + } + if initDerivedFrom.Number > inL1DerivedFrom.Number { + return nil, fmt.Errorf("msg %s was included in block %s derived from %s which is not in cross-safe scope %s: %w", + msg, includedIn, initDerivedFrom, inL1DerivedFrom, types.ErrOutOfScope) + } + } else if msg.Timestamp == candidate.Timestamp { + // If timestamp is equal: we have to inspect ordering of individual + // log events to ensure non-cyclic cross-chain message ordering. + // And since we may have back-and-forth messaging, we cannot wait till the initiating side is cross-safe. + // Thus check that it was included in a local-safe block, + // and then proceed with transitive block checks, + // to ensure the local block we depend on is becoming cross-safe also. + includedIn, err := d.Check(initChainID, msg.BlockNum, msg.LogIdx, msg.Hash) + if err != nil { + return nil, fmt.Errorf("executing msg %s failed check: %w", msg, err) + } + // As a hazard block, it will be checked to be included in a cross-safe block, + // or right after a cross-safe block in a local-safe block, in HazardSafeFrontierChecks. + if existing, ok := hazards[msg.Chain]; ok { + if existing != includedIn { + return nil, fmt.Errorf("found dependency on %s (chain %d), but already depend on %s", includedIn, initChainID, chainID) + } + } else { + // Mark it as hazard block + hazards[msg.Chain] = includedIn + } + } else { + // Timestamp invariant is broken: executing message tries to execute future block. + // The predeploy inbox contract should not have allowed this executing message through. + return nil, fmt.Errorf("executing message %s in %s breaks timestamp invariant", msg, candidate) + } + } + return hazards, nil +} diff --git a/op-supervisor/supervisor/backend/cross/safe_start_test.go b/op-supervisor/supervisor/backend/cross/safe_start_test.go new file mode 100644 index 000000000000..cb6bd4757214 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_start_test.go @@ -0,0 +1,341 @@ +package cross + +import ( + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestCrossSafeHazards(t *testing.T) { + t.Run("empty execMsgs", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{} + // when there are no execMsgs, + // no work is done, and no error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.NoError(t, err) + require.Empty(t, hazards) + }) + t.Run("CanExecuteAt returns false", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + canExecuteAtfn: func() (bool, error) { + return false, nil + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanExecuteAt returns false, + // no work is done and an error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("CanExecuteAt returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + canExecuteAtfn: func() (bool, error) { + return false, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanExecuteAt returns false, + // no work is done and an error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("unknown chain", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, types.ErrUnknownChain + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and ChainIDFromIndex returns ErrUnknownChain, + // an error is returned as a ErrConflict + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("ChainIDFromUInt64 returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and ChainIDFromIndex returns some other error, + // the error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("CanInitiateAt returns false", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + canInitiateAtfn: func() (bool, error) { + return false, nil + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanInitiateAt returns false, + // the error is returned as a ErrConflict + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("CanInitiateAt returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{ + canInitiateAtfn: func() (bool, error) { + return false, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanInitiateAt returns an error, + // the error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is greater than candidate", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 10} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is greater than the candidate, + // an error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "breaks timestamp invariant") + require.Empty(t, hazards) + }) + t.Run("timestamp is equal, Check returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timetamp is equal to the candidate, + // and check returns an error, + // that error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is equal, same hazard twice", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1, em2} + // when there are two execMsgs, and both are equal time to the candidate, + // and check returns the same includedIn for both + // they load the hazards once, and return no error + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.NoError(t, err) + require.Equal(t, hazards, map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): sampleBlockSeal}) + }) + t.Run("timestamp is equal, different hazards", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + // set the check function to return a different BlockSeal for the second call + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + sampleBlockSeal2 := types.BlockSeal{Number: 333, Hash: common.BytesToHash([]byte{0x22})} + calls := 0 + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + defer func() { calls++ }() + if calls == 0 { + return sampleBlockSeal, nil + } + return sampleBlockSeal2, nil + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1, em2} + // when there are two execMsgs, and both are equal time to the candidate, + // and check returns different includedIn for the two, + // an error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "but already depend on") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, check returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and check returns an error, + // that error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, DerivedFrom returns error", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + ssd.derivedFromFn = func() (derivedFrom types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and CrossDerivedFrom returns aan error, + // that error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, DerivedFrom Number is greater", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + sampleDerivedFrom := types.BlockSeal{Number: 4, Hash: common.BytesToHash([]byte{0x03})} + ssd.derivedFromFn = func() (derivedFrom types.BlockSeal, err error) { + return sampleDerivedFrom, nil + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and CrossDerivedFrom returns a BlockSeal with a greater Number than the inL1DerivedFrom, + // an error is returned as a ErrOutOfScope + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrOutOfScope) + require.Empty(t, hazards) + }) + t.Run("timestamp is less, DerivedFrom Number less", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + sampleDerivedFrom := types.BlockSeal{Number: 1, Hash: common.BytesToHash([]byte{0x03})} + ssd.derivedFromFn = func() (derivedFrom types.BlockSeal, err error) { + return sampleDerivedFrom, nil + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{Number: 10} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and CrossDerivedFrom returns a BlockSeal with a smaller Number than the inL1DerivedFrom, + // no error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.NoError(t, err) + require.Empty(t, hazards) + }) + t.Run("timestamp is less, DerivedFrom Number equal", func(t *testing.T) { + ssd := &mockSafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + ssd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + sampleDerivedFrom := types.BlockSeal{Number: 1, Hash: common.BytesToHash([]byte{0x03})} + ssd.derivedFromFn = func() (derivedFrom types.BlockSeal, err error) { + return sampleDerivedFrom, nil + } + ssd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + inL1DerivedFrom := eth.BlockID{Number: 1} + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and CrossDerivedFrom returns a BlockSeal with a equal to the Number of inL1DerivedFrom, + // no error is returned + hazards, err := CrossSafeHazards(ssd, chainID, inL1DerivedFrom, candidate, execMsgs) + require.NoError(t, err) + require.Empty(t, hazards) + }) +} + +type mockSafeStartDeps struct { + deps mockDependencySet + checkFn func() (includedIn types.BlockSeal, err error) + derivedFromFn func() (derivedFrom types.BlockSeal, err error) +} + +func (m *mockSafeStartDeps) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) { + if m.checkFn != nil { + return m.checkFn() + } + return types.BlockSeal{}, nil +} + +func (m *mockSafeStartDeps) CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { + if m.derivedFromFn != nil { + return m.derivedFromFn() + } + return types.BlockSeal{}, nil +} + +func (m *mockSafeStartDeps) DependencySet() depset.DependencySet { + return m.deps +} diff --git a/op-supervisor/supervisor/backend/cross/safe_update.go b/op-supervisor/supervisor/backend/cross/safe_update.go new file mode 100644 index 000000000000..172277db52de --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_update.go @@ -0,0 +1,116 @@ +package cross + +import ( + "context" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type CrossSafeDeps interface { + CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) + + SafeFrontierCheckDeps + SafeStartDeps + + CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) + NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) + PreviousDerived(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) + + OpenBlock(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) + + UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error +} + +func CrossSafeUpdate(ctx context.Context, logger log.Logger, chainID types.ChainID, d CrossSafeDeps) error { + logger.Debug("Cross-safe update call") + // TODO(#11693): establish L1 reorg-lock of scopeDerivedFrom + // defer unlock once we are done checking the chain + candidateScope, err := scopedCrossSafeUpdate(logger, chainID, d) + if err == nil { + // if we made progress, and no errors, then there is no need to bump the L1 scope yet. + return nil + } + if !errors.Is(err, types.ErrOutOfScope) { + return err + } + // candidateScope is expected to be set if ErrOutOfScope is returned. + if candidateScope == (eth.BlockRef{}) { + return fmt.Errorf("expected L1 scope to be defined with ErrOutOfScope: %w", err) + } + logger.Debug("Cross-safe updating ran out of L1 scope", "scope", candidateScope, "err", err) + // bump the L1 scope up, and repeat the prev L2 block, not the candidate + newScope, err := d.NextDerivedFrom(chainID, candidateScope.ID()) + if err != nil { + return fmt.Errorf("failed to identify new L1 scope to expand to after %s: %w", candidateScope, err) + } + _, currentCrossSafe, err := d.CrossSafe(chainID) + if err != nil { + // TODO: if genesis isn't cross-safe by default, then we can't register something as cross-safe here + return fmt.Errorf("failed to identify cross-safe scope to repeat: %w", err) + } + parent, err := d.PreviousDerived(chainID, currentCrossSafe.ID()) + if err != nil { + return fmt.Errorf("cannot find parent-block of cross-safe: %w", err) + } + crossSafeRef := currentCrossSafe.WithParent(parent.ID()) + logger.Debug("Bumping cross-safe scope", "scope", newScope, "crossSafe", crossSafeRef) + if err := d.UpdateCrossSafe(chainID, newScope, crossSafeRef); err != nil { + return fmt.Errorf("failed to update cross-safe head with L1 scope increment to %s and repeat of L2 block %s: %w", candidateScope, crossSafeRef, err) + } + return nil +} + +// scopedCrossSafeUpdate runs through the cross-safe update checks. +// If no L2 cross-safe progress can be made without additional L1 input data, +// then a types.ErrOutOfScope error is returned, +// with the current scope that will need to be expanded for further progress. +func scopedCrossSafeUpdate(logger log.Logger, chainID types.ChainID, d CrossSafeDeps) (scope eth.BlockRef, err error) { + candidateScope, candidate, err := d.CandidateCrossSafe(chainID) + if err != nil { + return candidateScope, fmt.Errorf("failed to determine candidate block for cross-safe: %w", err) + } + logger.Debug("Candidate cross-safe", "scope", candidateScope, "candidate", candidate) + opened, _, execMsgs, err := d.OpenBlock(chainID, candidate.Number) + if err != nil { + return candidateScope, fmt.Errorf("failed to open block %s: %w", candidate, err) + } + if opened.ID() != candidate.ID() { + return candidateScope, fmt.Errorf("unsafe L2 DB has %s, but candidate cross-safe was %s: %w", opened, candidate, types.ErrConflict) + } + hazards, err := CrossSafeHazards(d, chainID, candidateScope.ID(), types.BlockSealFromRef(opened), sliceOfExecMsgs(execMsgs)) + if err != nil { + return candidateScope, fmt.Errorf("failed to determine dependencies of cross-safe candidate %s: %w", candidate, err) + } + if err := HazardSafeFrontierChecks(d, candidateScope.ID(), hazards); err != nil { + return candidateScope, fmt.Errorf("failed to verify block %s in cross-safe frontier: %w", candidate, err) + } + //if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil { + // TODO + //} + + // promote the candidate block to cross-safe + if err := d.UpdateCrossSafe(chainID, candidateScope, candidate); err != nil { + return candidateScope, fmt.Errorf("failed to update cross-safe head to %s, derived from scope %s: %w", candidate, candidateScope, err) + } + return candidateScope, nil +} + +func NewCrossSafeWorker(logger log.Logger, chainID types.ChainID, d CrossSafeDeps) *Worker { + logger = logger.New("chain", chainID) + return NewWorker(logger, func(ctx context.Context) error { + return CrossSafeUpdate(ctx, logger, chainID, d) + }) +} + +func sliceOfExecMsgs(execMsgs map[uint32]*types.ExecutingMessage) []*types.ExecutingMessage { + msgs := make([]*types.ExecutingMessage, 0, len(execMsgs)) + for _, msg := range execMsgs { + msgs = append(msgs, msg) + } + return msgs +} diff --git a/op-supervisor/supervisor/backend/cross/safe_update_test.go b/op-supervisor/supervisor/backend/cross/safe_update_test.go new file mode 100644 index 000000000000..fec5f69b8ca4 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/safe_update_test.go @@ -0,0 +1,399 @@ +package cross + +import ( + "context" + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestCrossSafeUpdate(t *testing.T) { + t.Run("scopedCrossSafeUpdate passes", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + opened := eth.BlockRef{Number: 1} + execs := map[uint32]*types.ExecutingMessage{1: {}} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 10, execs, nil + } + csd.deps = mockDependencySet{} + // when scopedCrossSafeUpdate returns no error, + // no error is returned + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.NoError(t, err) + }) + t.Run("scopedCrossSafeUpdate reuturns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, errors.New("some error") + } + csd.deps = mockDependencySet{} + // when scopedCrossSafeUpdate returns an error, + // (by way of OpenBlock returning an error), + // the error is returned + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.ErrorContains(t, err, "some error") + }) + t.Run("scopedCrossSafeUpdate reuturns ErrOutOfScope", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, types.ErrOutOfScope + } + newScope := eth.BlockRef{Number: 3} + csd.nextDerivedFromFn = func(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) { + return newScope, nil + } + currentCrossSafe := types.BlockSeal{Number: 5} + csd.crossSafeFn = func(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { + return types.BlockSeal{}, currentCrossSafe, nil + } + parent := types.BlockSeal{Number: 4} + csd.previousDerivedFn = func(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) { + return parent, nil + } + csd.deps = mockDependencySet{} + var updatingChain types.ChainID + var updatingCandidateScope eth.BlockRef + var updatingCandidate eth.BlockRef + csd.updateCrossSafeFn = func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { + updatingChain = chain + updatingCandidateScope = l1View + updatingCandidate = lastCrossDerived + return nil + } + // when scopedCrossSafeUpdate returns Out of Scope error, + // CrossSafeUpdate proceeds anyway and calls UpdateCrossSafe + // the update uses the new scope returned by NextDerivedFrom + // and a crossSafeRef made from the current crossSafe and its parent + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.NoError(t, err) + require.Equal(t, chainID, updatingChain) + require.Equal(t, newScope, updatingCandidateScope) + crossSafeRef := currentCrossSafe.WithParent(parent.ID()) + require.Equal(t, crossSafeRef, updatingCandidate) + }) + t.Run("NextDerivedFrom returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, types.ErrOutOfScope + } + csd.nextDerivedFromFn = func(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) { + return eth.BlockRef{}, errors.New("some error") + } + csd.deps = mockDependencySet{} + // when scopedCrossSafeUpdate returns Out of Scope error, + // and NextDerivedFrom returns an error, + // the error is returned + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.ErrorContains(t, err, "some error") + }) + t.Run("PreviousDerived returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, types.ErrOutOfScope + } + csd.previousDerivedFn = func(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + csd.deps = mockDependencySet{} + // when scopedCrossSafeUpdate returns Out of Scope error, + // and PreviousDerived returns an error, + // the error is returned + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.ErrorContains(t, err, "some error") + }) + t.Run("UpdateCrossSafe returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, types.ErrOutOfScope + } + csd.updateCrossSafeFn = func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { + return errors.New("some error") + } + csd.deps = mockDependencySet{} + // when scopedCrossSafeUpdate returns Out of Scope error, + // and UpdateCrossSafe returns an error, + // the error is returned + err := CrossSafeUpdate(ctx, logger, chainID, csd) + require.ErrorContains(t, err, "some error") + }) +} + +func TestScopedCrossSafeUpdate(t *testing.T) { + t.Run("CandidateCrossSafe returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, eth.BlockRef{}, errors.New("some error") + } + // when CandidateCrossSafe returns an error, + // the error is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "some error") + require.Equal(t, eth.BlockRef{}, blockRef) + }) + t.Run("CandidateCrossSafe returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, errors.New("some error") + } + // when OpenBlock returns an error, + // the error is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "some error") + require.Equal(t, eth.BlockRef{}, blockRef) + }) + t.Run("candidate does not match opened block", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, candidate, nil + } + opened := eth.BlockRef{Number: 2} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 0, nil, nil + } + // when OpenBlock and CandidateCrossSafe return different blocks, + // an ErrConflict is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorIs(t, err, types.ErrConflict) + require.Equal(t, eth.BlockRef{}, blockRef) + }) + t.Run("CrossSafeHazards returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, candidate, nil + } + opened := eth.BlockRef{Number: 1} + execs := map[uint32]*types.ExecutingMessage{1: {}} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 10, execs, nil + } + // cause CrossSafeHazards to return an error by making ChainIDFromIndex return an error + csd.deps = mockDependencySet{} + csd.deps.chainIDFromIndexfn = func() (types.ChainID, error) { + return types.ChainID{}, errors.New("some error") + } + // when CrossSafeHazards returns an error, + // the error is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "some error") + require.ErrorContains(t, err, "dependencies of cross-safe candidate") + require.Equal(t, eth.BlockRef{}, blockRef) + }) + t.Run("HazardSafeFrontierChecks returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return eth.BlockRef{}, candidate, nil + } + opened := eth.BlockRef{Number: 1} + execs := map[uint32]*types.ExecutingMessage{1: {}} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 10, execs, nil + } + count := 0 + csd.deps = mockDependencySet{} + // cause CrossSafeHazards to return an error by making ChainIDFromIndex return an error + // but only on the second call (which will be used by HazardSafeFrontierChecks) + csd.deps.chainIDFromIndexfn = func() (types.ChainID, error) { + defer func() { count++ }() + if count == 0 { + return types.ChainID{}, nil + } + return types.ChainID{}, errors.New("some error") + } + // when CrossSafeHazards returns an error, + // the error is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "some error") + require.ErrorContains(t, err, "frontier") + require.Equal(t, eth.BlockRef{}, blockRef) + }) + t.Run("UpdateCrossSafe returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + opened := eth.BlockRef{Number: 1} + execs := map[uint32]*types.ExecutingMessage{1: {}} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 10, execs, nil + } + csd.deps = mockDependencySet{} + csd.updateCrossSafeFn = func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { + return errors.New("some error") + } + // when UpdateCrossSafe returns an error, + // the error is returned + _, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "some error") + require.ErrorContains(t, err, "failed to update") + }) + t.Run("successful update", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + opened := eth.BlockRef{Number: 1} + execs := map[uint32]*types.ExecutingMessage{1: {}} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 10, execs, nil + } + csd.deps = mockDependencySet{} + var updatingChain types.ChainID + var updatingCandidateScope eth.BlockRef + var updatingCandidate eth.BlockRef + csd.updateCrossSafeFn = func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { + updatingChain = chain + updatingCandidateScope = l1View + updatingCandidate = lastCrossDerived + return nil + } + // when no errors occur, the update is carried out + // the used candidate and scope are from CandidateCrossSafe + // the candidateScope is returned + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.Equal(t, chainID, updatingChain) + require.Equal(t, candidateScope, updatingCandidateScope) + require.Equal(t, candidate, updatingCandidate) + require.Equal(t, candidateScope, blockRef) + require.NoError(t, err) + }) +} + +type mockCrossSafeDeps struct { + deps mockDependencySet + crossSafeFn func(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) + candidateCrossSafeFn func() (derivedFromScope, crossSafe eth.BlockRef, err error) + openBlockFn func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) + updateCrossSafeFn func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error + nextDerivedFromFn func(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) + previousDerivedFn func(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) +} + +func (m *mockCrossSafeDeps) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { + if m.crossSafeFn != nil { + return m.crossSafeFn(chainID) + } + return types.BlockSeal{}, types.BlockSeal{}, nil +} + +func (m *mockCrossSafeDeps) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) { + if m.candidateCrossSafeFn != nil { + return m.candidateCrossSafeFn() + } + return eth.BlockRef{}, eth.BlockRef{}, nil +} + +func (m *mockCrossSafeDeps) DependencySet() depset.DependencySet { + return m.deps +} + +func (m *mockCrossSafeDeps) CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { + return types.BlockSeal{}, nil +} + +func (m *mockCrossSafeDeps) Check(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{}, nil +} + +func (m *mockCrossSafeDeps) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) { + if m.nextDerivedFromFn != nil { + return m.nextDerivedFromFn(chain, derivedFrom) + } + return eth.BlockRef{}, nil +} + +func (m *mockCrossSafeDeps) PreviousDerived(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) { + if m.previousDerivedFn != nil { + return m.previousDerivedFn(chain, derived) + } + return types.BlockSeal{}, nil +} + +func (m *mockCrossSafeDeps) OpenBlock(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + if m.openBlockFn != nil { + return m.openBlockFn(chainID, blockNum) + } + return eth.BlockRef{}, 0, nil, nil +} + +func (m *mockCrossSafeDeps) UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { + if m.updateCrossSafeFn != nil { + return m.updateCrossSafeFn(chain, l1View, lastCrossDerived) + } + return nil +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_frontier.go b/op-supervisor/supervisor/backend/cross/unsafe_frontier.go new file mode 100644 index 000000000000..5e30da999eb1 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_frontier.go @@ -0,0 +1,63 @@ +package cross + +import ( + "errors" + "fmt" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type UnsafeFrontierCheckDeps interface { + ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (parent eth.BlockID, err error) + + IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error + IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error + + DependencySet() depset.DependencySet +} + +// HazardUnsafeFrontierChecks verifies all the hazard blocks are either: +// - already cross-unsafe. +// - the first (if not first: local blocks to verify before proceeding) +// local-unsafe block, after the cross-unsafe block. +func HazardUnsafeFrontierChecks(d UnsafeFrontierCheckDeps, hazards map[types.ChainIndex]types.BlockSeal) error { + depSet := d.DependencySet() + for hazardChainIndex, hazardBlock := range hazards { + hazardChainID, err := depSet.ChainIDFromIndex(hazardChainIndex) + if err != nil { + if errors.Is(err, types.ErrUnknownChain) { + err = fmt.Errorf("cannot cross-unsafe verify block %s of unknown chain index %s: %w", hazardBlock, hazardChainIndex, types.ErrConflict) + } + return err + } + // Anything we depend on in this timestamp must be cross-unsafe already, or the first block after. + err = d.IsCrossUnsafe(hazardChainID, hazardBlock.ID()) + if err != nil { + if errors.Is(err, types.ErrFuture) { + // Not already cross-unsafe, so we check if the block is local-unsafe + // (a sanity check if part of the canonical chain). + err = d.IsLocalUnsafe(hazardChainID, hazardBlock.ID()) + if err != nil { + // can be ErrFuture (missing data) or ErrConflict (non-canonical) + return fmt.Errorf("hazard block %s (chain %d) is not local-unsafe: %w", hazardBlock, hazardChainID, err) + } + // If it doesn't have a parent block, then there is no prior block required to be cross-safe + if hazardBlock.Number > 0 { + // Check that parent of hazardBlockID is cross-safe within view + parent, err := d.ParentBlock(hazardChainID, hazardBlock.ID()) + if err != nil { + return fmt.Errorf("failed to retrieve parent-block of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err) + } + if err := d.IsCrossUnsafe(hazardChainID, parent); err != nil { + return fmt.Errorf("cannot rely on hazard-block %s (chain %s), parent block %s is not cross-unsafe: %w", hazardBlock, hazardChainID, parent, err) + } + } + } else { + return fmt.Errorf("failed to determine cross-derived of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err) + } + } + } + return nil +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_frontier_test.go b/op-supervisor/supervisor/backend/cross/unsafe_frontier_test.go new file mode 100644 index 000000000000..09086e0e8738 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_frontier_test.go @@ -0,0 +1,130 @@ +package cross + +import ( + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestHazardUnsafeFrontierChecks(t *testing.T) { + t.Run("empty hazards", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{} + // when there are no hazards, + // no work is done, and no error is returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.NoError(t, err) + }) + t.Run("unknown chain", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{ + deps: mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, types.ErrUnknownChain + }, + }, + } + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + // when there is one hazard, and ChainIDFromIndex returns ErrUnknownChain, + // an error is returned as a ErrConflict + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.ErrorIs(t, err, types.ErrConflict) + }) + t.Run("is cross unsafe", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + ufcd.isCrossUnsafe = nil + // when there is one hazard, and IsCrossUnsafe returns nil (no error) + // no error is returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.NoError(t, err) + }) + t.Run("errFuture: is not local unsafe", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + ufcd.isCrossUnsafe = types.ErrFuture + ufcd.isLocalUnsafe = errors.New("some error") + // when there is one hazard, and IsCrossUnsafe returns an ErrFuture, + // and IsLocalUnsafe returns an error, + // the error from IsLocalUnsafe is (wrapped and) returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.ErrorContains(t, err, "some error") + }) + t.Run("errFuture: genesis block", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {}} + ufcd.isCrossUnsafe = types.ErrFuture + // when there is one hazard, and IsCrossUnsafe returns an ErrFuture, + // BUT the hazard's block number is 0, + // no error is returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.NoError(t, err) + }) + t.Run("errFuture: error getting parent block", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3}} + ufcd.isCrossUnsafe = types.ErrFuture + ufcd.parentBlockFn = func() (parent eth.BlockID, err error) { + return eth.BlockID{}, errors.New("some error") + } + // when there is one hazard, and IsCrossUnsafe returns an ErrFuture, + // and there is an error getting the parent block, + // the error from ParentBlock is (wrapped and) returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.ErrorContains(t, err, "some error") + }) + t.Run("errFuture: parent block is not cross unsafe", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3}} + ufcd.isCrossUnsafe = types.ErrFuture + ufcd.parentBlockFn = func() (parent eth.BlockID, err error) { + // when getting the parent block, prep isCrossSafe to be err + ufcd.isCrossUnsafe = errors.New("not cross unsafe!") + return eth.BlockID{}, nil + } + // when there is one hazard, and IsCrossUnsafe returns an ErrFuture, + // and the parent block is not cross unsafe, + // the error from IsCrossUnsafe is (wrapped and) returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.ErrorContains(t, err, "not cross unsafe!") + }) + t.Run("IsCrossUnsafe Error", func(t *testing.T) { + ufcd := &mockUnsafeFrontierCheckDeps{} + hazards := map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): {Number: 3, Hash: common.BytesToHash([]byte{0x02})}} + ufcd.isCrossUnsafe = errors.New("some error") + // when there is one hazard, and IsCrossUnsafe returns an error, + // the error from IsCrossUnsafe is (wrapped and) returned + err := HazardUnsafeFrontierChecks(ufcd, hazards) + require.ErrorContains(t, err, "some error") + }) +} + +type mockUnsafeFrontierCheckDeps struct { + deps mockDependencySet + parentBlockFn func() (parent eth.BlockID, err error) + isCrossUnsafe error + isLocalUnsafe error +} + +func (m *mockUnsafeFrontierCheckDeps) DependencySet() depset.DependencySet { + return m.deps +} + +func (m *mockUnsafeFrontierCheckDeps) ParentBlock(chainID types.ChainID, block eth.BlockID) (parent eth.BlockID, err error) { + if m.parentBlockFn != nil { + return m.parentBlockFn() + } + return eth.BlockID{}, nil +} + +func (m *mockUnsafeFrontierCheckDeps) IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error { + return m.isCrossUnsafe +} + +func (m *mockUnsafeFrontierCheckDeps) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error { + return m.isLocalUnsafe +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_start.go b/op-supervisor/supervisor/backend/cross/unsafe_start.go new file mode 100644 index 000000000000..9b4568a7b622 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_start.go @@ -0,0 +1,100 @@ +package cross + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type UnsafeStartDeps interface { + Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) + + IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error + + DependencySet() depset.DependencySet +} + +// CrossUnsafeHazards checks if the given messages all exist and pass invariants. +// It returns a hazard-set: if any intra-block messaging happened, +// these hazard blocks have to be verified. +func CrossUnsafeHazards(d UnsafeStartDeps, chainID types.ChainID, + candidate types.BlockSeal, execMsgs []*types.ExecutingMessage) (hazards map[types.ChainIndex]types.BlockSeal, err error) { + + hazards = make(map[types.ChainIndex]types.BlockSeal) + + // Warning for future: If we have sub-second distinct blocks (different block number), + // we need to increase precision on the above timestamp invariant. + // Otherwise a local block can depend on a future local block of the same chain, + // simply by pulling in a block of another chain, + // which then depends on a block of the original chain, + // all with the same timestamp, without message cycles. + + depSet := d.DependencySet() + + if len(execMsgs) > 0 { + if ok, err := depSet.CanExecuteAt(chainID, candidate.Timestamp); err != nil { + return nil, fmt.Errorf("cannot check message execution of block %s (chain %s): %w", candidate, chainID, err) + } else if !ok { + return nil, fmt.Errorf("cannot execute messages in block %s (chain %s): %w", candidate, chainID, types.ErrConflict) + } + } + + // check all executing messages + for _, msg := range execMsgs { + initChainID, err := depSet.ChainIDFromIndex(msg.Chain) + if err != nil { + if errors.Is(err, types.ErrUnknownChain) { + err = fmt.Errorf("msg %s may not execute from unknown chain %s: %w", msg, msg.Chain, types.ErrConflict) + } + return nil, err + } + if ok, err := depSet.CanInitiateAt(initChainID, msg.Timestamp); err != nil { + return nil, fmt.Errorf("cannot check message initiation of msg %s (chain %s): %w", msg, chainID, err) + } else if !ok { + return nil, fmt.Errorf("cannot allow initiating message %s (chain %s): %w", msg, chainID, types.ErrConflict) + } + if msg.Timestamp < candidate.Timestamp { + // If timestamp is older: invariant ensures non-cyclic ordering relative to other messages. + // Check that the block that they are included in is cross-safe already. + includedIn, err := d.Check(initChainID, msg.BlockNum, msg.LogIdx, msg.Hash) + if err != nil { + return nil, fmt.Errorf("executing msg %s failed check: %w", msg, err) + } + if err := d.IsCrossUnsafe(initChainID, includedIn.ID()); err != nil { + return nil, fmt.Errorf("msg %s included in non-cross-unsafe block %s: %w", msg, includedIn, err) + } + } else if msg.Timestamp == candidate.Timestamp { + // If timestamp is equal: we have to inspect ordering of individual + // log events to ensure non-cyclic cross-chain message ordering. + // And since we may have back-and-forth messaging, we cannot wait till the initiating side is cross-unsafe. + // Thus check that it was included in a local-unsafe block, + // and then proceed with transitive block checks, + // to ensure the local block we depend on is becoming cross-unsafe also. + includedIn, err := d.Check(initChainID, msg.BlockNum, msg.LogIdx, msg.Hash) + if err != nil { + return nil, fmt.Errorf("executing msg %s failed check: %w", msg, err) + } + + // As a hazard block, it will be checked to be included in a cross-unsafe block, + // or right after a cross-unsafe block, in HazardUnsafeFrontierChecks. + if existing, ok := hazards[msg.Chain]; ok { + if existing != includedIn { + return nil, fmt.Errorf("found dependency on %s (chain %d), but already depend on %s", includedIn, initChainID, chainID) + } + } else { + // Mark it as hazard block + hazards[msg.Chain] = includedIn + } + } else { + // Timestamp invariant is broken: executing message tries to execute future block. + // The predeploy inbox contract should not have allowed this executing message through. + return nil, fmt.Errorf("executing message %s in %s breaks timestamp invariant", msg, candidate) + } + } + return hazards, nil +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_start_test.go b/op-supervisor/supervisor/backend/cross/unsafe_start_test.go new file mode 100644 index 000000000000..123fc70abe17 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_start_test.go @@ -0,0 +1,280 @@ +package cross + +import ( + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestCrossUnsafeHazards(t *testing.T) { + t.Run("empty execMsgs", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{} + // when there are no execMsgs, + // no work is done, and no error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.NoError(t, err) + require.Empty(t, hazards) + }) + t.Run("CanExecuteAt returns false", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + canExecuteAtfn: func() (bool, error) { + return false, nil + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanExecuteAt returns false, + // no work is done and an error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("CanExecuteAt returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + canExecuteAtfn: func() (bool, error) { + return false, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanExecuteAt returns false, + // no work is done and an error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("unknown chain", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, types.ErrUnknownChain + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and ChainIDFromIndex returns ErrUnknownChain, + // an error is returned as a ErrConflict + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("ChainIDFromUInt64 returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + chainIDFromIndexfn: func() (types.ChainID, error) { + return types.ChainID{}, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and ChainIDFromIndex returns some other error, + // the error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("CanInitiateAt returns false", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + canInitiateAtfn: func() (bool, error) { + return false, nil + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanInitiateAt returns false, + // the error is returned as a ErrConflict + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorIs(t, err, types.ErrConflict) + require.Empty(t, hazards) + }) + t.Run("CanInitiateAt returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{ + canInitiateAtfn: func() (bool, error) { + return false, errors.New("some error") + }, + } + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{} + execMsgs := []*types.ExecutingMessage{{}} + // when there is one execMsg, and CanInitiateAt returns an error, + // the error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is greater than candidate", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 10} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is greater than the candidate, + // an error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "breaks timestamp invariant") + require.Empty(t, hazards) + }) + t.Run("timestamp is equal, Check returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timetamp is equal to the candidate, + // and check returns an error, + // that error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is equal, same hazard twice", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1, em2} + // when there are two execMsgs, and both are equal time to the candidate, + // and check returns the same includedIn for both + // they load the hazards once, and return no error + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.NoError(t, err) + require.Equal(t, hazards, map[types.ChainIndex]types.BlockSeal{types.ChainIndex(0): sampleBlockSeal}) + }) + t.Run("timestamp is equal, different hazards", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + // set the check function to return a different BlockSeal for the second call + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + sampleBlockSeal2 := types.BlockSeal{Number: 333, Hash: common.BytesToHash([]byte{0x22})} + calls := 0 + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + defer func() { calls++ }() + if calls == 0 { + return sampleBlockSeal, nil + } + return sampleBlockSeal2, nil + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 2} + execMsgs := []*types.ExecutingMessage{em1, em2} + // when there are two execMsgs, and both are equal time to the candidate, + // and check returns different includedIn for the two, + // an error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "but already depend on") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, check returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + return types.BlockSeal{}, errors.New("some error") + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and check returns an error, + // that error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, IsCrossUnsafe returns error", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + usd.isCrossUnsafeFn = func() error { + return errors.New("some error") + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and IsCrossUnsafe returns an error, + // that error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.ErrorContains(t, err, "some error") + require.Empty(t, hazards) + }) + t.Run("timestamp is less, IsCrossUnsafe", func(t *testing.T) { + usd := &mockUnsafeStartDeps{} + sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} + usd.checkFn = func() (includedIn types.BlockSeal, err error) { + return sampleBlockSeal, nil + } + usd.isCrossUnsafeFn = func() error { + return nil + } + usd.deps = mockDependencySet{} + chainID := types.ChainIDFromUInt64(0) + candidate := types.BlockSeal{Timestamp: 2} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1} + execMsgs := []*types.ExecutingMessage{em1} + // when there is one execMsg, and the timestamp is less than the candidate, + // and IsCrossUnsafe returns no error, + // no error is returned + hazards, err := CrossUnsafeHazards(usd, chainID, candidate, execMsgs) + require.NoError(t, err) + require.Empty(t, hazards) + }) +} + +type mockUnsafeStartDeps struct { + deps mockDependencySet + checkFn func() (includedIn types.BlockSeal, err error) + isCrossUnsafeFn func() error +} + +func (m *mockUnsafeStartDeps) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) { + if m.checkFn != nil { + return m.checkFn() + } + return types.BlockSeal{}, nil +} + +func (m *mockUnsafeStartDeps) IsCrossUnsafe(chainID types.ChainID, derived eth.BlockID) error { + if m.isCrossUnsafeFn != nil { + return m.isCrossUnsafeFn() + } + return nil +} + +func (m *mockUnsafeStartDeps) DependencySet() depset.DependencySet { + return m.deps +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_update.go b/op-supervisor/supervisor/backend/cross/unsafe_update.go new file mode 100644 index 000000000000..bc13146d38ff --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_update.go @@ -0,0 +1,79 @@ +package cross + +import ( + "context" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type CrossUnsafeDeps interface { + CrossUnsafe(chainID types.ChainID) (types.BlockSeal, error) + + UnsafeStartDeps + UnsafeFrontierCheckDeps + + OpenBlock(chainID types.ChainID, blockNum uint64) (block eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) + + UpdateCrossUnsafe(chain types.ChainID, crossUnsafe types.BlockSeal) error +} + +func CrossUnsafeUpdate(ctx context.Context, logger log.Logger, chainID types.ChainID, d CrossUnsafeDeps) error { + var candidate types.BlockSeal + var execMsgs []*types.ExecutingMessage + + // fetch cross-head to determine next cross-unsafe candidate + if crossUnsafe, err := d.CrossUnsafe(chainID); err != nil { + if errors.Is(err, types.ErrFuture) { + // If genesis / no cross-safe block yet, then defer update + logger.Debug("No cross-unsafe starting point yet") + return nil + } else { + return err + } + } else { + // Open block N+1: this is a local-unsafe block, + // just after cross-safe, that can be promoted if it passes the dependency checks. + bl, _, msgs, err := d.OpenBlock(chainID, crossUnsafe.Number+1) + if err != nil { + return fmt.Errorf("failed to open block %d: %w", crossUnsafe.Number+1, err) + } + if bl.ParentHash != crossUnsafe.Hash { + return fmt.Errorf("cannot use block %s, it does not build on cross-unsafe block %s: %w", bl, crossUnsafe, types.ErrConflict) + } + candidate = types.BlockSealFromRef(bl) + execMsgs = sliceOfExecMsgs(msgs) + } + + hazards, err := CrossUnsafeHazards(d, chainID, candidate, execMsgs) + if err != nil { + // TODO(#11693): reorgs can be detected by checking if the error is ErrConflict, + // missing data is identified by ErrFuture, + // and other errors (e.g. DB issues) are identifier by remaining error kinds. + return fmt.Errorf("failed to check for cross-chain hazards: %w", err) + } + + if err := HazardUnsafeFrontierChecks(d, hazards); err != nil { + return fmt.Errorf("failed to verify block %s in cross-unsafe frontier: %w", candidate, err) + } + //if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil { + //// TODO + //} + + // promote the candidate block to cross-unsafe + if err := d.UpdateCrossUnsafe(chainID, candidate); err != nil { + return fmt.Errorf("failed to update cross-unsafe head to %s: %w", candidate, err) + } + return nil +} + +func NewCrossUnsafeWorker(logger log.Logger, chainID types.ChainID, d CrossUnsafeDeps) *Worker { + logger = logger.New("chain", chainID) + return NewWorker(logger, func(ctx context.Context) error { + return CrossUnsafeUpdate(ctx, logger, chainID, d) + }) +} diff --git a/op-supervisor/supervisor/backend/cross/unsafe_update_test.go b/op-supervisor/supervisor/backend/cross/unsafe_update_test.go new file mode 100644 index 000000000000..c0e264c97206 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/unsafe_update_test.go @@ -0,0 +1,212 @@ +package cross + +import ( + "context" + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestCrossUnsafeUpdate(t *testing.T) { + t.Run("CrossUnsafe returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return types.BlockSeal{}, errors.New("some error") + } + usd.deps = mockDependencySet{} + // when an error is returned by CrossUnsafe, + // the error is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorContains(t, err, "some error") + }) + t.Run("CrossUnsafe returns ErrFuture", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return types.BlockSeal{}, types.ErrFuture + } + usd.deps = mockDependencySet{} + // when a ErrFuture is returned by CrossUnsafe, + // no error is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.NoError(t, err) + }) + t.Run("OpenBlock returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return eth.BlockRef{}, 0, nil, errors.New("some error") + } + usd.deps = mockDependencySet{} + // when an error is returned by OpenBlock, + // the error is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorContains(t, err, "some error") + }) + t.Run("opened block parent hash does not match", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + crossUnsafe := types.BlockSeal{Hash: common.Hash{0x11}} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return crossUnsafe, nil + } + bl := eth.BlockRef{ParentHash: common.Hash{0x01}} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return bl, 0, nil, nil + } + usd.deps = mockDependencySet{} + // when the parent hash of the opened block does not match the cross-unsafe block, + // an ErrConflict is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorIs(t, err, types.ErrConflict) + }) + t.Run("CrossSafeHazards returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + crossUnsafe := types.BlockSeal{Hash: common.Hash{0x01}} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return crossUnsafe, nil + } + bl := eth.BlockRef{ParentHash: common.Hash{0x01}} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + // include one executing message to trigger the CanExecuteAt check + return bl, 0, map[uint32]*types.ExecutingMessage{1: {}}, nil + } + usd.deps = mockDependencySet{} + // make CrossSafeHazards return an error by setting CanExecuteAtfn to return an error + usd.deps.canExecuteAtfn = func() (bool, error) { + return false, errors.New("some error") + } + // when CrossSafeHazards returns an error, + // the error is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorContains(t, err, "some error") + }) + t.Run("HazardUnsafeFrontierChecks returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + crossUnsafe := types.BlockSeal{Hash: common.Hash{0x01}} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return crossUnsafe, nil + } + bl := eth.BlockRef{ParentHash: common.Hash{0x01}, Time: 1} + em1 := &types.ExecutingMessage{Timestamp: 1} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + // include one executing message to ensure one hazard is returned + return bl, 0, map[uint32]*types.ExecutingMessage{1: em1}, nil + } + usd.deps = mockDependencySet{} + count := 0 + // make HazardUnsafeFrontierChecks return an error by failing the second ChainIDFromIndex call + // (the first one is in CrossSafeHazards) + usd.deps.chainIDFromIndexfn = func() (types.ChainID, error) { + defer func() { count++ }() + if count == 1 { + return types.ChainID{}, errors.New("some error") + } + return types.ChainID{}, nil + } + // when HazardUnsafeFrontierChecks returns an error, + // the error is returned + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorContains(t, err, "some error") + }) + t.Run("successful update", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + crossUnsafe := types.BlockSeal{Hash: common.Hash{0x01}} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return crossUnsafe, nil + } + bl := eth.BlockRef{ParentHash: common.Hash{0x01}, Time: 1} + em1 := &types.ExecutingMessage{Timestamp: 1} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + // include one executing message to ensure one hazard is returned + return bl, 0, map[uint32]*types.ExecutingMessage{1: em1}, nil + } + usd.deps = mockDependencySet{} + var updatingChainID types.ChainID + var updatingBlock types.BlockSeal + usd.updateCrossUnsafeFn = func(chain types.ChainID, crossUnsafe types.BlockSeal) error { + updatingChainID = chain + updatingBlock = crossUnsafe + return nil + } + // when there are no errors, the cross-unsafe block is updated + // the updated block is the block opened in OpenBlock + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.NoError(t, err) + require.Equal(t, chainID, updatingChainID) + require.Equal(t, types.BlockSealFromRef(bl), updatingBlock) + }) +} + +type mockCrossUnsafeDeps struct { + deps mockDependencySet + crossUnsafeFn func(chainID types.ChainID) (types.BlockSeal, error) + openBlockFn func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) + updateCrossUnsafeFn func(chain types.ChainID, crossUnsafe types.BlockSeal) error +} + +func (m *mockCrossUnsafeDeps) CrossUnsafe(chainID types.ChainID) (derived types.BlockSeal, err error) { + if m.crossUnsafeFn != nil { + return m.crossUnsafeFn(chainID) + } + return types.BlockSeal{}, nil +} + +func (m *mockCrossUnsafeDeps) DependencySet() depset.DependencySet { + return m.deps +} + +func (m *mockCrossUnsafeDeps) Check(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{}, nil +} + +func (m *mockCrossUnsafeDeps) OpenBlock(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + if m.openBlockFn != nil { + return m.openBlockFn(chainID, blockNum) + } + return eth.BlockRef{}, 0, nil, nil +} + +func (m *mockCrossUnsafeDeps) UpdateCrossUnsafe(chain types.ChainID, block types.BlockSeal) error { + if m.updateCrossUnsafeFn != nil { + return m.updateCrossUnsafeFn(chain, block) + } + return nil +} + +func (m *mockCrossUnsafeDeps) IsCrossUnsafe(chainID types.ChainID, blockNum eth.BlockID) error { + return nil +} + +func (m *mockCrossUnsafeDeps) IsLocalUnsafe(chainID types.ChainID, blockNum eth.BlockID) error { + return nil +} + +func (m *mockCrossUnsafeDeps) ParentBlock(chainID types.ChainID, blockNum eth.BlockID) (eth.BlockID, error) { + return eth.BlockID{}, nil +} diff --git a/op-supervisor/supervisor/backend/cross/worker.go b/op-supervisor/supervisor/backend/cross/worker.go new file mode 100644 index 000000000000..1342d7048fab --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/worker.go @@ -0,0 +1,103 @@ +package cross + +import ( + "context" + "errors" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +// Worker iterates work +type Worker struct { + log log.Logger + + // workFn is the function to call to process the scope + workFn workFn + + // channel with capacity of 1, full if there is work to do + poke chan struct{} + pollDuration time.Duration + + // lifetime management of the chain processor + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup +} + +// workFn is a function used by the worker +// it is opaque to the worker, and is set by the constructor +type workFn func(ctx context.Context) error + +// NewWorker creates a new worker to process updates +func NewWorker(log log.Logger, workFn workFn) *Worker { + ctx, cancel := context.WithCancel(context.Background()) + out := &Worker{ + log: log, + poke: make(chan struct{}, 1), + // The data may have changed, and we may have missed a poke, so re-attempt regularly. + pollDuration: time.Second * 4, + ctx: ctx, + cancel: cancel, + } + out.workFn = workFn + return out +} + +func (s *Worker) StartBackground() { + s.wg.Add(1) + go s.worker() +} + +func (s *Worker) ProcessWork() error { + return s.workFn(s.ctx) +} + +func (s *Worker) worker() { + defer s.wg.Done() + + delay := time.NewTicker(s.pollDuration) + for { + if s.ctx.Err() != nil { // check if we are closing down + return + } + + // do the work + err := s.workFn(s.ctx) + if err != nil { + if errors.Is(err, s.ctx.Err()) { + return + } + s.log.Error("Failed to process work", "err", err) + } + + // await next time we process, or detect shutdown + select { + case <-s.ctx.Done(): + delay.Stop() + return + case <-s.poke: + s.log.Debug("Continuing cross-safe verification after hint of new data") + continue + case <-delay.C: + s.log.Debug("Checking for cross-safe updates") + continue + } + } +} + +func (s *Worker) OnNewData() error { + // signal that we have something to process + select { + case s.poke <- struct{}{}: + default: + // already requested an update + } + return nil +} + +func (s *Worker) Close() { + s.cancel() + s.wg.Wait() +} diff --git a/op-supervisor/supervisor/backend/cross/worker_test.go b/op-supervisor/supervisor/backend/cross/worker_test.go new file mode 100644 index 000000000000..64f4d2d8d12f --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/worker_test.go @@ -0,0 +1,106 @@ +package cross + +import ( + "context" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestWorker(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + t.Run("do work", func(t *testing.T) { + count := 0 + w := NewWorker(logger, func(ctx context.Context) error { + count++ + return nil + }) + t.Cleanup(w.Close) + // when ProcessWork is called, the workFn is called once + require.NoError(t, w.ProcessWork()) + require.Equal(t, 1, count) + }) + t.Run("background worker", func(t *testing.T) { + count := 0 + w := NewWorker(logger, func(ctx context.Context) error { + count++ + return nil + }) + t.Cleanup(w.Close) + // set a long poll duration so the worker does not auto-run + w.pollDuration = 100 * time.Second + // when StartBackground is called, the worker runs in the background + // the count should increment once + w.StartBackground() + require.Eventually(t, func() bool { + return count == 1 + }, 2*time.Second, 100*time.Millisecond) + }) + t.Run("background worker OnNewData", func(t *testing.T) { + count := 0 + w := NewWorker(logger, func(ctx context.Context) error { + count++ + return nil + }) + t.Cleanup(w.Close) + // set a long poll duration so the worker does not auto-run + w.pollDuration = 100 * time.Second + // when StartBackground is called, the worker runs in the background + // the count should increment once + w.StartBackground() + require.Eventually(t, func() bool { + return count == 1 + }, 2*time.Second, 100*time.Millisecond) + // when OnNewData is called, the worker runs again + require.NoError(t, w.OnNewData()) + require.Eventually(t, func() bool { + return count == 2 + }, 2*time.Second, 100*time.Millisecond) + // and due to the long poll duration, the worker does not run again + require.Never(t, func() bool { + return count > 2 + }, 10*time.Second, 100*time.Millisecond) + }) + t.Run("background fast poll", func(t *testing.T) { + count := 0 + w := NewWorker(logger, func(ctx context.Context) error { + count++ + return nil + }) + t.Cleanup(w.Close) + // set a long poll duration so the worker does not auto-run + w.pollDuration = 100 * time.Millisecond + // when StartBackground is called, the worker runs in the background + // the count should increment rapidly and reach 10 in 1 second + w.StartBackground() + require.Eventually(t, func() bool { + return count == 10 + }, 2*time.Second, 100*time.Millisecond) + }) + t.Run("close", func(t *testing.T) { + count := 0 + w := NewWorker(logger, func(ctx context.Context) error { + count++ + return nil + }) + t.Cleanup(w.Close) // close on cleanup in case of early error + // set a long poll duration so the worker does not auto-run + w.pollDuration = 100 * time.Millisecond + // when StartBackground is called, the worker runs in the background + // the count should increment rapidly and reach 10 in 1 second + w.StartBackground() + require.Eventually(t, func() bool { + return count == 10 + }, 2*time.Second, 100*time.Millisecond) + // once the worker is closed, it stops running + // and the count does not increment + w.Close() + stopCount := count + require.Never(t, func() bool { + return count != stopCount + }, 3*time.Second, 100*time.Millisecond) + }) +} diff --git a/op-supervisor/supervisor/backend/processors/chain_processor.go b/op-supervisor/supervisor/backend/processors/chain_processor.go index d27c26fe2bbc..db57e977aae0 100644 --- a/op-supervisor/supervisor/backend/processors/chain_processor.go +++ b/op-supervisor/supervisor/backend/processors/chain_processor.go @@ -17,8 +17,6 @@ import ( "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) -var ErrNoRPCSource = errors.New("no RPC client configured") - type Source interface { L1BlockRefByNumber(ctx context.Context, number uint64) (eth.L1BlockRef, error) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, gethtypes.Receipts, error) @@ -137,7 +135,7 @@ func (s *ChainProcessor) work() { if err := s.update(target); err != nil { if errors.Is(err, ethereum.NotFound) { s.log.Info("Cannot find next block yet", "target", target) - } else if errors.Is(err, ErrNoRPCSource) { + } else if errors.Is(err, types.ErrNoRPCSource) { s.log.Warn("No RPC source configured, cannot process new blocks") } else { s.log.Error("Failed to process new block", "err", err) @@ -158,7 +156,7 @@ func (s *ChainProcessor) update(nextNum uint64) error { defer s.clientLock.Unlock() if s.client == nil { - return ErrNoRPCSource + return types.ErrNoRPCSource } ctx, cancel := context.WithTimeout(s.ctx, time.Second*10) diff --git a/op-supervisor/supervisor/backend/processors/log_processor_test.go b/op-supervisor/supervisor/backend/processors/log_processor_test.go index de28b1f6f508..e23ad6933f74 100644 --- a/op-supervisor/supervisor/backend/processors/log_processor_test.go +++ b/op-supervisor/supervisor/backend/processors/log_processor_test.go @@ -108,7 +108,7 @@ func TestLogProcessor(t *testing.T) { }, } execMsg := types.ExecutingMessage{ - Chain: 4, + Chain: 4, // TODO(#11105): translate chain ID to chain index BlockNum: 6, LogIdx: 8, Timestamp: 10, From d69e922bc9d0dfef3e55e16bbfe91681863d0849 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 25 Oct 2024 15:54:24 +0100 Subject: [PATCH 032/451] add interfaces for safe contracts (#12650) * add interfaces for safe contracts, use named imports for DeputyGuardianModule * fix failing test --- packages/contracts-bedrock/semver-lock.json | 4 +-- .../src/safe/DeputyGuardianModule.sol | 6 ++-- .../safe/interfaces/IDeputyGuardianModule.sol | 32 +++++++++++++++++ .../src/safe/interfaces/ILivenessGuard.sol | 32 +++++++++++++++++ .../src/safe/interfaces/ILivenessModule.sol | 34 +++++++++++++++++++ .../test/universal/Specs.t.sol | 3 +- 6 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 packages/contracts-bedrock/src/safe/interfaces/IDeputyGuardianModule.sol create mode 100644 packages/contracts-bedrock/src/safe/interfaces/ILivenessGuard.sol create mode 100644 packages/contracts-bedrock/src/safe/interfaces/ILivenessModule.sol diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 3ba87ad738b0..712a014680e4 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -196,8 +196,8 @@ "sourceCodeHash": "0x3a0a294932d6deba043f6a2b46b4e8477ee96e7fb054d7e7229a43ce4352c68d" }, "src/safe/DeputyGuardianModule.sol": { - "initCodeHash": "0x308212d163aad169a5e42ce703a1ce36f5425ad96037850c0747177041f6596e", - "sourceCodeHash": "0xde1a289c1cb0bf92138daf8f3db7457be2f84bedaa111b536f646dd6e121718c" + "initCodeHash": "0xd95e562f395d4eb6e332f4474dffab660ada9e9da7c79f58fb6052278e0904df", + "sourceCodeHash": "0x45daabe094de0287e244e6fea4f1887b9adc09b07c47dc77361b1678645a1470" }, "src/safe/LivenessGuard.sol": { "initCodeHash": "0x9ac0b039b1591f7c00cf11cb758d118c9b42e6e08250b619d6b6fd605a43d5ee", diff --git a/packages/contracts-bedrock/src/safe/DeputyGuardianModule.sol b/packages/contracts-bedrock/src/safe/DeputyGuardianModule.sol index 0882910d5072..673cf2a1efb6 100644 --- a/packages/contracts-bedrock/src/safe/DeputyGuardianModule.sol +++ b/packages/contracts-bedrock/src/safe/DeputyGuardianModule.sol @@ -7,7 +7,7 @@ import { Enum } from "safe-contracts/common/Enum.sol"; // Libraries import { Unauthorized } from "src/libraries/PortalErrors.sol"; -import "src/dispute/lib/Types.sol"; +import { GameType, Timestamp } from "src/dispute/lib/Types.sol"; // Interfaces import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; @@ -48,8 +48,8 @@ contract DeputyGuardianModule is ISemver { address internal immutable DEPUTY_GUARDIAN; /// @notice Semantic version. - /// @custom:semver 2.0.1-beta.3 - string public constant version = "2.0.1-beta.3"; + /// @custom:semver 2.0.1-beta.4 + string public constant version = "2.0.1-beta.4"; // Constructor to initialize the Safe and baseModule instances constructor(Safe _safe, ISuperchainConfig _superchainConfig, address _deputyGuardian) { diff --git a/packages/contracts-bedrock/src/safe/interfaces/IDeputyGuardianModule.sol b/packages/contracts-bedrock/src/safe/interfaces/IDeputyGuardianModule.sol new file mode 100644 index 000000000000..da74cf4dd983 --- /dev/null +++ b/packages/contracts-bedrock/src/safe/interfaces/IDeputyGuardianModule.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; +import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; +import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; +import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { GameType, Timestamp } from "src/dispute/lib/Types.sol"; +import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; + +interface IDeputyGuardianModule is ISemver { + error ExecutionFailed(string); + error Unauthorized(); + + event Paused(string identifier); + event Unpaused(); + event DisputeGameBlacklisted(IDisputeGame indexed game); + event RespectedGameTypeSet(GameType indexed gameType, Timestamp indexed updatedAt); + + function version() external view returns (string memory); + function __constructor__(Safe _safe, ISuperchainConfig _superchainConfig, address _deputyGuardian) external; + function safe() external view returns (Safe safe_); + function superchainConfig() external view returns (ISuperchainConfig superchainConfig_); + function deputyGuardian() external view returns (address deputyGuardian_); + function pause() external; + function unpause() external; + function setAnchorState(IAnchorStateRegistry _registry, IFaultDisputeGame _game) external; + function blacklistDisputeGame(IOptimismPortal2 _portal, IDisputeGame _game) external; + function setRespectedGameType(IOptimismPortal2 _portal, GameType _gameType) external; +} diff --git a/packages/contracts-bedrock/src/safe/interfaces/ILivenessGuard.sol b/packages/contracts-bedrock/src/safe/interfaces/ILivenessGuard.sol new file mode 100644 index 000000000000..a69921211955 --- /dev/null +++ b/packages/contracts-bedrock/src/safe/interfaces/ILivenessGuard.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; +import { Enum } from "safe-contracts/common/Enum.sol"; +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + +interface ILivenessGuard is ISemver { + event OwnerRecorded(address owner); + + function lastLive(address) external view returns (uint256); + function version() external view returns (string memory); + function __constructor__(Safe _safe) external; + + function safe() external view returns (Safe safe_); + function checkTransaction( + address _to, + uint256 _value, + bytes memory _data, + Enum.Operation _operation, + uint256 _safeTxGas, + uint256 _baseGas, + uint256 _gasPrice, + address _gasToken, + address payable _refundReceiver, + bytes memory _signatures, + address _msgSender + ) + external; + function checkAfterExecution(bytes32, bool) external; + function showLiveness() external; +} diff --git a/packages/contracts-bedrock/src/safe/interfaces/ILivenessModule.sol b/packages/contracts-bedrock/src/safe/interfaces/ILivenessModule.sol new file mode 100644 index 000000000000..43711d508597 --- /dev/null +++ b/packages/contracts-bedrock/src/safe/interfaces/ILivenessModule.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; +import { LivenessGuard } from "src/safe/LivenessGuard.sol"; +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + +interface ILivenessModule is ISemver { + error OwnerRemovalFailed(string); + + event RemovedOwner(address indexed owner); + event OwnershipTransferredToFallback(); + + function ownershipTransferredToFallback() external view returns (bool); + function version() external view returns (string memory); + function __constructor__( + Safe _safe, + LivenessGuard _livenessGuard, + uint256 _livenessInterval, + uint256 _minOwners, + uint256 _thresholdPercentage, + address _fallbackOwner + ) + external; + function getRequiredThreshold(uint256 _numOwners) external view returns (uint256 threshold_); + function safe() external view returns (Safe safe_); + function livenessGuard() external view returns (LivenessGuard livenessGuard_); + function livenessInterval() external view returns (uint256 livenessInterval_); + function minOwners() external view returns (uint256 minOwners_); + function thresholdPercentage() external view returns (uint256 thresholdPercentage_); + function fallbackOwner() external view returns (address fallbackOwner_); + function canRemove(address _owner) external view returns (bool canRemove_); + function removeOwners(address[] memory _previousOwners, address[] memory _ownersToRemove) external; +} diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index f6f6b989896b..9c22e178a5f4 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -953,12 +953,13 @@ contract Specification_Test is CommonTest { /// @notice Ensures that there's an auth spec for every L1 contract function. function testContractAuth() public { - string[] memory pathExcludes = new string[](5); + string[] memory pathExcludes = new string[](6); pathExcludes[0] = "src/dispute/interfaces/*"; pathExcludes[1] = "src/dispute/lib/*"; pathExcludes[2] = "src/safe/SafeSigners.sol"; pathExcludes[3] = "src/L1/interfaces/*"; pathExcludes[4] = "src/governance/interfaces/*"; + pathExcludes[5] = "src/safe/interfaces/*"; Abi[] memory abis = ForgeArtifacts.getContractFunctionAbis( "src/{L1,dispute,governance,safe,universal/ProxyAdmin.sol,universal/WETH98.sol}", pathExcludes ); From 02d5832349a1f469adb00191459333e2032b09b4 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 25 Oct 2024 22:29:57 +0700 Subject: [PATCH 033/451] maint(ct): rm fee vault withdrawal script (#12638) Removes the FeeVaultWithdrawal.s.sol script that was previously used to trigger withdrawals manually. All of this is automated now and you can just use Etherscan if you really want to do things manually so the value of the script is minimal. --- .../scripts/ops/FeeVaultWithdrawal.s.sol | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 packages/contracts-bedrock/scripts/ops/FeeVaultWithdrawal.s.sol diff --git a/packages/contracts-bedrock/scripts/ops/FeeVaultWithdrawal.s.sol b/packages/contracts-bedrock/scripts/ops/FeeVaultWithdrawal.s.sol deleted file mode 100644 index 9e5bb96cfe31..000000000000 --- a/packages/contracts-bedrock/scripts/ops/FeeVaultWithdrawal.s.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { console } from "forge-std/console.sol"; -import { Script } from "forge-std/Script.sol"; -import { IMulticall3 } from "forge-std/interfaces/IMulticall3.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { IFeeVault } from "src/L2/interfaces/IFeeVault.sol"; - -/// @title FeeVaultWithdrawal -/// @notice A script to make it very simple to withdraw from the fee vaults. -/// The usage is as follows: -/// $ forge script scripts/FeeVaultWithdrawal.s.sol \ -/// --rpc-url $ETH_RPC_URL --broadcast \ -/// --private-key $PRIVATE_KEY -contract FeeVaultWithdrawal is Script { - IMulticall3 private constant multicall = IMulticall3(MULTICALL3_ADDRESS); - IMulticall3.Call3[] internal calls; - - /// @notice The entrypoint function. Determines which FeeVaults can be withdrawn from and then - /// will send the transaction via Multicall3 to withdraw all FeeVaults. - function run() external { - require(address(multicall).code.length > 0); - - address[] memory vaults = new address[](3); - vaults[0] = Predeploys.SEQUENCER_FEE_WALLET; - vaults[1] = Predeploys.BASE_FEE_VAULT; - vaults[2] = Predeploys.L1_FEE_VAULT; - - for (uint256 i; i < vaults.length; i++) { - address vault = vaults[i]; - bool shouldCall = canWithdrawal(vault); - if (shouldCall) { - calls.push( - IMulticall3.Call3({ - target: vault, - allowFailure: false, - callData: abi.encodeWithSelector(IFeeVault.withdraw.selector) - }) - ); - - address recipient = IFeeVault(payable(vault)).RECIPIENT(); - uint256 balance = vault.balance; - log(balance, recipient, vault); - } else { - string memory logline = - string.concat(vm.toString(vault), " does not have a large enough balance to withdraw."); - console.log(logline); - } - } - - if (calls.length > 0) { - vm.broadcast(); - multicall.aggregate3(calls); - console.log("Success."); - } - } - - /// @notice Checks whether or not a FeeVault can be withdrawn. The balance of the account must - /// be larger than the `MIN_WITHDRAWAL_AMOUNT`. - function canWithdrawal(address _vault) internal view returns (bool) { - uint256 minWithdrawalAmount = IFeeVault(payable(_vault)).MIN_WITHDRAWAL_AMOUNT(); - uint256 balance = _vault.balance; - return balance >= minWithdrawalAmount; - } - - /// @notice Logs the information relevant to the user. - function log(uint256 _balance, address _recipient, address _vault) internal pure { - string memory logline = string.concat( - "Withdrawing ", vm.toString(_balance), " to ", vm.toString(_recipient), " from ", vm.toString(_vault) - ); - console.log(logline); - } -} From a095d32da8ddfd1a9007a7db8a78dea2c3b1cff2 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Sat, 26 Oct 2024 05:00:57 +0100 Subject: [PATCH 034/451] fix failing interfaces (#12648) * fix failing interfaces * fix crossl2inbox, revert some changes * fix semgrep error --- .../contracts-bedrock/scripts/L2Genesis.s.sol | 4 - .../scripts/checks/interfaces/main.go | 5 +- packages/contracts-bedrock/semver-lock.json | 44 +++++----- .../snapshots/abi/CrossL2Inbox.json | 6 +- .../abi/L2ToL2CrossDomainMessenger.json | 2 +- .../src/L1/L1StandardBridge.sol | 4 +- .../contracts-bedrock/src/L2/CrossL2Inbox.sol | 16 +++- .../src/L2/L2ERC721Bridge.sol | 4 +- .../src/L2/L2StandardBridge.sol | 4 +- .../src/L2/L2StandardBridgeInterop.sol | 4 +- .../src/L2/L2ToL2CrossDomainMessenger.sol | 19 ++--- .../src/L2/interfaces/ICrossL2Inbox.sol | 69 ++++++++++------ .../IL2ToL2CrossDomainMessenger.sol | 73 ++++++++++++++--- .../src/L2/interfaces/ISuperchainWETH.sol | 7 +- .../src/dispute/FaultDisputeGame.sol | 4 +- .../src/dispute/interfaces/IDelayedWETH.sol | 10 ++- .../src/legacy/LegacyMintableERC20.sol | 2 +- .../src/universal/OptimismMintableERC20.sol | 29 ++----- .../OptimismMintableERC20Factory.sol | 4 +- .../src/universal/OptimismMintableERC721.sol | 40 ++++++--- .../OptimismMintableERC721Factory.sol | 4 +- .../src/universal/StandardBridge.sol | 3 +- .../src/universal/WETH98.sol | 71 ++++++++++++---- .../interfaces/ILegacyMintableERC20.sol | 17 ++++ .../interfaces/IOptimismMintableERC20.sol | 13 --- .../interfaces/IOptimismMintableERC721.sol | 72 +++++++++++------ .../src/universal/interfaces/IWETH.sol | 81 ------------------- .../src/universal/interfaces/IWETH98.sol | 39 +++++++++ .../test/L2/CrossL2Inbox.t.sol | 41 ++++------ .../test/L2/L2ToL2CrossDomainMessenger.t.sol | 44 +++++----- .../contracts-bedrock/test/setup/Setup.sol | 4 +- .../universal/OptimismMintableERC20.t.sol | 3 +- .../test/universal/StandardBridge.t.sol | 2 +- semgrep/sol-rules.yaml | 3 + 34 files changed, 418 insertions(+), 329 deletions(-) create mode 100644 packages/contracts-bedrock/src/universal/interfaces/ILegacyMintableERC20.sol delete mode 100644 packages/contracts-bedrock/src/universal/interfaces/IWETH.sol create mode 100644 packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 4c4079d78064..207c59732146 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -32,10 +32,6 @@ import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMesseng import { IGasPriceOracle } from "src/L2/interfaces/IGasPriceOracle.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; -interface IInitializable { - function initialize(address _addr) external; -} - struct L1Dependencies { address payable l1CrossDomainMessengerProxy; address payable l1StandardBridgeProxy; diff --git a/packages/contracts-bedrock/scripts/checks/interfaces/main.go b/packages/contracts-bedrock/scripts/checks/interfaces/main.go index 73d3af062707..5d62a2e3b4ff 100644 --- a/packages/contracts-bedrock/scripts/checks/interfaces/main.go +++ b/packages/contracts-bedrock/scripts/checks/interfaces/main.go @@ -26,9 +26,8 @@ var excludeContracts = []string{ "IEAS", "ISchemaResolver", "ISchemaRegistry", // TODO: Interfaces that need to be fixed - "IInitializable", "ILegacyMintableERC20", "IOptimismMintableERC20", - "IOptimismMintableERC721", "KontrolCheatsBase", "IWETH", "IDelayedWETH", "ISuperchainWETH", - "IL2ToL2CrossDomainMessenger", "ICrossL2Inbox", "ISystemConfigInterop", "IResolvedDelegateProxy", + "IInitializable", "IOptimismMintableERC20", "ILegacyMintableERC20", + "KontrolCheatsBase", "ISystemConfigInterop", "IResolvedDelegateProxy", } type ContractDefinition struct { diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 712a014680e4..57567ec570c7 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -16,8 +16,8 @@ "sourceCodeHash": "0x289de9f40898b6305deecc6b60cdf566aa6c6a1444f713c3a0af23ea7878207e" }, "src/L1/L1StandardBridge.sol": { - "initCodeHash": "0x2868b09ecbe9f2bbc885605c2886b4c79f1c8e4171626c63776603b1b84698a8", - "sourceCodeHash": "0xc03da137b3ea72e0109fb284229283b21a0303104afbe37d2fe86ad806392a7f" + "initCodeHash": "0x802f72745bb9a82dc049377bb9cf6b58f35aec388aeb957b28a5e14f28d91bc1", + "sourceCodeHash": "0x24b784645b065a5393a2115a078d67f91eb09afd5e70baf81daf975381f16155" }, "src/L1/L2OutputOracle.sol": { "initCodeHash": "0x1182bfb87c4ab399b912ca7fe18cdbf4b24c414e078fb0a55bd3c44d442d3ed1", @@ -60,8 +60,8 @@ "sourceCodeHash": "0x983e8e248c61e362ba6a01dd2e217a535c9bb828dc0b4421f5f27e0577f2e14c" }, "src/L2/CrossL2Inbox.sol": { - "initCodeHash": "0x66b052adce7e9194d054952d67d08b53964120067600358243ec86c85b90877b", - "sourceCodeHash": "0x38e6127ec6be99eb8c38c2c9d6e82761b33dde446bba250dc2c1b84983449e4e" + "initCodeHash": "0x31ecaebf368ab3333e80c6dc004b3c9f9a31f813c3138ab388bb3eead9f1b4ee", + "sourceCodeHash": "0xa1779d84a14332dcdd167293171d0fe2629d759a23d7cc34ffe2bde7e1605dbc" }, "src/L2/ETHLiquidity.sol": { "initCodeHash": "0x713c18f95a6a746d0703f475f3ae10c106c9b9ecb64d881a2e61b8969b581371", @@ -88,24 +88,24 @@ "sourceCodeHash": "0x56edf0f36366326a92722ae3c7502bce3d80b2ee5e354181dc09ba801437a488" }, "src/L2/L2ERC721Bridge.sol": { - "initCodeHash": "0xb8236514beabcc1830e5e7e9eccb965e30d01f8bbb5a2d8b4f3d241c2d12819b", - "sourceCodeHash": "0x268e34289b7b8a5d1785af1907bd07d1c8ce4a9e3ea1685bdd9a77904dab3557" + "initCodeHash": "0xaed0528e8b81817a0c3b41513c02e7fd678f58e34b98f02ea33d5a770a064c2f", + "sourceCodeHash": "0xf8569c75b801f38f8a5a41e94e90f159ddc5f5412804b26e3e564755a50631b8" }, "src/L2/L2StandardBridge.sol": { - "initCodeHash": "0x651eed10044d0b19b7e4eba864345df15e252be1401f39a552ec0d2f9c4df064", - "sourceCodeHash": "0xb55e58b5d4912edf05026878a5f5ac8019372212ed2a77843775d595fbf51b84" + "initCodeHash": "0xcb4aa19f0cd43a35cb5c65f26c3cfd7c41f1d1e5bcc15aef6096d385df7272c9", + "sourceCodeHash": "0x89771b53b7f6e64d943afb2a4bf15395efcf20d5302b76a18e52fa7cce8cdc56" }, "src/L2/L2StandardBridgeInterop.sol": { - "initCodeHash": "0xd43d07c2ba5a73af56181c0221c28f3b495851b03cf8e35f9b009857bb9538a6", - "sourceCodeHash": "0x36087332a4365ee172eed8fa35aa48e804b08279e97332058a588c2d4ae30c6b" + "initCodeHash": "0xc4eaece28d2cfca3c51247c3cce320a167a83c7fd13aea5736549d2b25e0b139", + "sourceCodeHash": "0x9e80044adf5f83c30b520ee153b75be5a152081c9e1271e7e618ecfccd1fb4ac" }, "src/L2/L2ToL1MessagePasser.sol": { "initCodeHash": "0x13fe3729beb9ed966c97bef09acb9fe5043fe651d453145073d05f2567fa988d", "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x6eb3718548d97b69c201a75c27a3a5a77400ebd5e0a9bdcc06e2cc28f9b4a689", - "sourceCodeHash": "0x4bb08a8a9d5de37d1fb2dd8a9bcc483305510c32508f0be2d54757e6e257aedb" + "initCodeHash": "0x2a1a1ee4f47175ce661ee8e4e50cfa879b082dcb5278b1d66ddda00ed77bb744", + "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" }, "src/L2/OptimismSuperchainERC20.sol": { "initCodeHash": "0x24d85d246858d1aff78ae86c614dd0dc0f63b3326b2b662e3462c3a6f9b7965e", @@ -164,8 +164,8 @@ "sourceCodeHash": "0x9cb0851b6e471461f2bb369bd72eef4cffe8a0d1345546608a2aa6795540211d" }, "src/dispute/FaultDisputeGame.sol": { - "initCodeHash": "0xf97d35adc72c7bcbb5415ff1b183af0a4e3c951696d1dde213df61df50c848b9", - "sourceCodeHash": "0x27b4ab6f75004d01ff177b2b26e500a8ad06e906fcd36b920daa067f27f97da6" + "initCodeHash": "0x1480098a19f71ce6b4f4548880c8794402315ed3efa6793241a3df0fae864205", + "sourceCodeHash": "0x2f084f3f0e52017beb2ecf571178b94ba885cca987ada472d9e178b486a91d9e" }, "src/legacy/DeployerWhitelist.sol": { "initCodeHash": "0x0b8177ed75b69eddbb9ce6537683f69a9935efed86a1d6faa8feaafbd151c1bd", @@ -208,20 +208,20 @@ "sourceCodeHash": "0xd1479c60087f352385b6d5379ef3cc07839f671d617626b4c94ece91da781ef2" }, "src/universal/OptimismMintableERC20.sol": { - "initCodeHash": "0x28c88484e1932253d6d12954492ac8a70744dc15c84429089af9944e5b158fd9", - "sourceCodeHash": "0x740b4043436d1b314ee3ba145acfcde60b6abd8416ea594f2b8e890b5d0bce6b" + "initCodeHash": "0x9cd677275b175812f1d5f90a127dbf7b3592714fd842a7a0de3988d716ca3eac", + "sourceCodeHash": "0x5611d8082f68af566554d7f09640b4b1f0e3efee4da1372b68fc7fc538a35ac7" }, "src/universal/OptimismMintableERC20Factory.sol": { - "initCodeHash": "0x9cd4102d3ca811d5dc67ae99ce7de95812264575a789f96a6057600e55dcab64", - "sourceCodeHash": "0xc70c8c11d6e754eabe746bbee47a5e1051f71f7a83913f62ebcce8db989a1357" + "initCodeHash": "0x03ad07bd7f89a29f1850fa8b5d377daf0e1d5aef6cb458a127df520549e8e8e6", + "sourceCodeHash": "0xdb6ec93782a4a217475195507740794a4f5553b9032e7ba31dc48b81f579a940" }, "src/universal/OptimismMintableERC721.sol": { - "initCodeHash": "0xec037be7fc28e072944b0a9e55d4278b92d6c68ccb41049ab52eafca59c6e023", - "sourceCodeHash": "0x5ea7c1b0cef5609f25c4193f5795fc9ce8f3ae08dbbf2945afe38e5af58f32c3" + "initCodeHash": "0x8aa309f2676d5267b6c9e411f88dc6e4badce414b8d66b330df3f60e9836380e", + "sourceCodeHash": "0x03bf7ad4d2b751bdead9930fc8f89b8e55d40dd4b2f5670fd339e87ae81f8b49" }, "src/universal/OptimismMintableERC721Factory.sol": { - "initCodeHash": "0x63d2ceafd3f3b3b54e31749574563e8022fef9c6da7bb8c7a113b3dbf571cfa2", - "sourceCodeHash": "0x705e270925fcad14e805b5ec1bbbb9e78b5b44e3b128f284b0113a4d68c82ef6" + "initCodeHash": "0x5ea977ba35558c3b75bebe28900548c763d205e40d6cf7660292b8e96bf3aea8", + "sourceCodeHash": "0x063ca3a0a2e3c592173af6157e383b5aaeff752000f98648a5c71260bb26590a" }, "src/universal/StorageSetter.sol": { "initCodeHash": "0x21b3059e9b13b330f76d02b61f61dcfa3abf3517a0b56afa0895c4b8291740bf", diff --git a/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json b/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json index 6f8c10e82eed..47eeab3f07f1 100644 --- a/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json +++ b/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json @@ -55,7 +55,7 @@ "type": "uint256" } ], - "internalType": "struct ICrossL2Inbox.Identifier", + "internalType": "struct Identifier", "name": "_id", "type": "tuple" }, @@ -164,7 +164,7 @@ "type": "uint256" } ], - "internalType": "struct ICrossL2Inbox.Identifier", + "internalType": "struct Identifier", "name": "_id", "type": "tuple" }, @@ -230,7 +230,7 @@ } ], "indexed": false, - "internalType": "struct ICrossL2Inbox.Identifier", + "internalType": "struct Identifier", "name": "id", "type": "tuple" } diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index daefa3a74386..6434233930db 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -99,7 +99,7 @@ "type": "uint256" } ], - "internalType": "struct ICrossL2Inbox.Identifier", + "internalType": "struct Identifier", "name": "_id", "type": "tuple" }, diff --git a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol index 6dd648f0d541..54cc833fe673 100644 --- a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol @@ -75,8 +75,8 @@ contract L1StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 2.2.1-beta.1 - string public constant version = "2.2.1-beta.1"; + /// @custom:semver 2.2.1-beta.2 + string public constant version = "2.2.1-beta.2"; /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; diff --git a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol index 7939dccddbb4..b330dfb0ceb4 100644 --- a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol +++ b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.25; import { Predeploys } from "src/libraries/Predeploys.sol"; import { TransientContext, TransientReentrancyAware } from "src/libraries/TransientContext.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { IDependencySet } from "src/L2/interfaces/IDependencySet.sol"; import { IL1BlockInterop } from "src/L2/interfaces/IL1BlockInterop.sol"; @@ -30,12 +29,21 @@ error TargetCallFailed(); /// @notice Thrown when trying to execute a cross chain message on a deposit transaction. error NoExecutingDeposits(); +/// @notice The struct for a pointer to a message payload in a remote (or local) chain. +struct Identifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; +} + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000022 /// @title CrossL2Inbox /// @notice The CrossL2Inbox is responsible for executing a cross chain message on the destination /// chain. It is permissionless to execute a cross chain message on behalf of any user. -contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware { +contract CrossL2Inbox is ISemver, TransientReentrancyAware { /// @notice Storage slot that the interop start timestamp is stored at. /// Equal to bytes32(uint256(keccak256("crossl2inbox.interopstart")) - 1) bytes32 internal constant INTEROP_START_SLOT = 0x5c769ee0ee8887661922049dc52480bb60322d765161507707dd9b190af5c149; @@ -65,8 +73,8 @@ contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware { address internal constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.8 - string public constant version = "1.0.0-beta.8"; + /// @custom:semver 1.0.0-beta.9 + string public constant version = "1.0.0-beta.9"; /// @notice Emitted when a cross chain message is being executed. /// @param msgHash Hash of message payload being executed. diff --git a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol index 7db9cc17aaa7..85c6856e629d 100644 --- a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol @@ -26,8 +26,8 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// wait for the one-week challenge period to elapse before their Optimism-native NFT /// can be refunded on L2. contract L2ERC721Bridge is ERC721Bridge, ISemver { - /// @custom:semver 1.8.0-beta.1 - string public constant version = "1.8.0-beta.1"; + /// @custom:semver 1.8.0-beta.2 + string public constant version = "1.8.0-beta.2"; /// @notice Constructs the L2ERC721Bridge contract. constructor() ERC721Bridge() { diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol index 1c7e2e307cc3..63bda3209fbb 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol @@ -58,9 +58,9 @@ contract L2StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.11.1-beta.2 + /// @custom:semver 1.11.1-beta.3 function version() public pure virtual returns (string memory) { - return "1.11.1-beta.2"; + return "1.11.1-beta.3"; } /// @notice Constructs the L2StandardBridge contract. diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol index eb25a406ede7..e17ef29dd964 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol @@ -40,9 +40,9 @@ contract L2StandardBridgeInterop is L2StandardBridge { event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); /// @notice Semantic version. - /// @custom:semver +interop-beta.1 + /// @custom:semver +interop-beta.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.1"); + return string.concat(super.version(), "+interop-beta.2"); } /// @notice Converts `amount` of `from` token to `to` token. diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index c8afe8be5d92..6b1d7327dbc0 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -4,9 +4,7 @@ pragma solidity 0.8.25; import { Encoding } from "src/libraries/Encoding.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol"; -import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol"; -import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; +import { CrossL2Inbox, Identifier } from "src/L2/CrossL2Inbox.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { TransientReentrancyAware } from "src/libraries/TransientContext.sol"; @@ -47,7 +45,7 @@ error TargetCallFailed(); /// @notice The L2ToL2CrossDomainMessenger is a higher level abstraction on top of the CrossL2Inbox that provides /// features necessary for secure transfers ERC20 tokens between L2 chains. Messages sent through the /// L2ToL2CrossDomainMessenger on the source chain receive both replay protection as well as domain binding. -contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, TransientReentrancyAware { +contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { /// @notice Storage slot for the sender of the current cross domain message. /// Equal to bytes32(uint256(keccak256("l2tol2crossdomainmessenger.sender")) - 1) bytes32 internal constant CROSS_DOMAIN_MESSAGE_SENDER_SLOT = @@ -67,8 +65,8 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again. @@ -153,14 +151,7 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra /// currently being replayed. /// @param _id Identifier of the SentMessage event to be relayed /// @param _sentMessage Message payload of the `SentMessage` event - function relayMessage( - ICrossL2Inbox.Identifier calldata _id, - bytes calldata _sentMessage - ) - external - payable - nonReentrant - { + function relayMessage(Identifier calldata _id, bytes calldata _sentMessage) external payable nonReentrant { // Ensure the log came from the messenger. Since the log origin is the CDM, there isn't a scenario where // this can be invoked from the CrossL2Inbox as the SentMessage log is not calldata for this function if (_id.origin != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { diff --git a/packages/contracts-bedrock/src/L2/interfaces/ICrossL2Inbox.sol b/packages/contracts-bedrock/src/L2/interfaces/ICrossL2Inbox.sol index 3267122fc0b1..bbd8c09afd8c 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ICrossL2Inbox.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ICrossL2Inbox.sol @@ -1,53 +1,70 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +struct Identifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; +} + /// @title ICrossL2Inbox /// @notice Interface for the CrossL2Inbox contract. interface ICrossL2Inbox { - /// @notice The struct for a pointer to a message payload in a remote (or local) chain. - struct Identifier { - address origin; - uint256 blockNumber; - uint256 logIndex; - uint256 timestamp; - uint256 chainId; - } + error ReentrantCall(); + + /// @notice Thrown when the caller is not DEPOSITOR_ACCOUNT when calling `setInteropStart()` + error NotDepositor(); + + /// @notice Thrown when attempting to set interop start when it's already set. + error InteropStartAlreadySet(); + + /// @notice Thrown when a non-written transient storage slot is attempted to be read from. + error NotEntered(); + + /// @notice Thrown when trying to execute a cross chain message with an invalid Identifier timestamp. + error InvalidTimestamp(); + + /// @notice Thrown when trying to execute a cross chain message with an invalid Identifier chain ID. + error InvalidChainId(); + + /// @notice Thrown when trying to execute a cross chain message and the target call fails. + error TargetCallFailed(); + + /// @notice Thrown when trying to execute a cross chain message on a deposit transaction. + error NoExecutingDeposits(); + + event ExecutingMessage(bytes32 indexed msgHash, Identifier id); + + function version() external view returns (string memory); /// @notice Returns the interop start timestamp. /// @return interopStart_ interop start timestamp. function interopStart() external view returns (uint256 interopStart_); /// @notice Returns the origin address of the Identifier. - /// @return origin_ The origin address of the Identifier. - function origin() external view returns (address origin_); + function origin() external view returns (address); /// @notice Returns the block number of the Identifier. - /// @return blockNumber_ The block number of the Identifier. - function blockNumber() external view returns (uint256 blockNumber_); + function blockNumber() external view returns (uint256); /// @notice Returns the log index of the Identifier. - /// @return logIndex_ The log index of the Identifier. - function logIndex() external view returns (uint256 logIndex_); + function logIndex() external view returns (uint256); /// @notice Returns the timestamp of the Identifier. - /// @return timestamp_ The timestamp of the Identifier. - function timestamp() external view returns (uint256 timestamp_); + function timestamp() external view returns (uint256); /// @notice Returns the chain ID of the Identifier. - /// @return chainId_ The chain ID of the Identifier. - function chainId() external view returns (uint256 chainId_); + function chainId() external view returns (uint256); + + function setInteropStart() external; /// @notice Executes a cross chain message on the destination chain. /// @param _id An Identifier pointing to the initiating message. /// @param _target Account that is called with _msg. - /// @param _msg The message payload, matching the initiating message. - function executeMessage( - ICrossL2Inbox.Identifier calldata _id, - address _target, - bytes calldata _msg - ) - external - payable; + /// @param _message The message payload, matching the initiating message. + function executeMessage(Identifier calldata _id, address _target, bytes calldata _message) external payable; /// @notice Validates a cross chain message on the destination chain /// and emits an ExecutingMessage event. This function is useful diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol index f8a803d78be8..00ca4906b5c4 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol @@ -1,17 +1,70 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol"; +struct Identifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; +} /// @title IL2ToL2CrossDomainMessenger /// @notice Interface for the L2ToL2CrossDomainMessenger contract. interface IL2ToL2CrossDomainMessenger { + /// @notice Thrown when a non-written slot in transient storage is attempted to be read from. + error NotEntered(); + + /// @notice Thrown when attempting to relay a message where payload origin is not L2ToL2CrossDomainMessenger. + error IdOriginNotL2ToL2CrossDomainMessenger(); + + /// @notice Thrown when the payload provided to the relay is not a SentMessage event. + error EventPayloadNotSentMessage(); + + /// @notice Thrown when attempting to send a message to the chain that the message is being sent from. + error MessageDestinationSameChain(); + + /// @notice Thrown when attempting to relay a message whose destination chain is not the chain relaying it. + error MessageDestinationNotRelayChain(); + + /// @notice Thrown when attempting to relay a message whose target is CrossL2Inbox. + error MessageTargetCrossL2Inbox(); + + /// @notice Thrown when attempting to relay a message whose target is L2ToL2CrossDomainMessenger. + error MessageTargetL2ToL2CrossDomainMessenger(); + + /// @notice Thrown when attempting to relay a message that has already been relayed. + error MessageAlreadyRelayed(); + + /// @notice Thrown when a reentrant call is detected. + error ReentrantCall(); + + /// @notice Thrown when a call to the target contract during message relay fails. + error TargetCallFailed(); + + /// @notice Emitted whenever a message is sent to a destination + /// @param destination Chain ID of the destination chain. + /// @param target Target contract or wallet address. + /// @param messageNonce Nonce associated with the messsage sent + /// @param sender Address initiating this message call + /// @param message Message payload to call target with. + event SentMessage( + uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message + ); + + /// @notice Emitted whenever a message is successfully relayed on this chain. + /// @param source Chain ID of the source chain. + /// @param messageNonce Nonce associated with the messsage sent + /// @param messageHash Hash of the message that was relayed. + event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash); + + function version() external view returns (string memory); + /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only /// be present in this mapping if it has successfully been relayed on this chain, and /// can therefore not be relayed again. - /// @param _msgHash message hash to check. /// @return Returns true if the message corresponding to the `_msgHash` was successfully relayed. - function successfulMessages(bytes32 _msgHash) external view returns (bool); + function successfulMessages(bytes32) external view returns (bool); /// @notice Retrieves the next message nonce. Message version will be added to the upper two /// bytes of the message nonce. Message version allows us to treat messages as having @@ -41,18 +94,16 @@ interface IL2ToL2CrossDomainMessenger { /// @param _message Message to trigger the target address with. /// @return msgHash_ The hash of the message being sent, which can be used for tracking whether /// the message has successfully been relayed. - function sendMessage( - uint256 _destination, - address _target, - bytes calldata _message - ) - external - returns (bytes32 msgHash_); + function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32); /// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only /// be executed via cross-chain call from the other messenger OR if the message was /// already received once and is currently being replayed. /// @param _id Identifier of the SentMessage event to be relayed /// @param _sentMessage Message payload of the `SentMessage` event - function relayMessage(ICrossL2Inbox.Identifier calldata _id, bytes calldata _sentMessage) external payable; + function relayMessage(Identifier calldata _id, bytes calldata _sentMessage) external payable; + + function messageVersion() external view returns (uint16); + + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol index 4c91322c6b12..1935b4d5b0b5 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IWETH } from "src/universal/interfaces/IWETH.sol"; +import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -interface ISuperchainWETH is IWETH, ICrosschainERC20, ISemver { +interface ISuperchainWETH is IWETH98, ICrosschainERC20, ISemver { error Unauthorized(); error NotCustomGasToken(); + function balanceOf(address src) external view returns (uint256); + function withdraw(uint256 _amount) external; + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol index 884fb00b4a65..9466465bd7ff 100644 --- a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol @@ -147,8 +147,8 @@ contract FaultDisputeGame is Clone, ISemver { uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8; /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.5 - string public constant version = "1.3.1-beta.5"; + /// @custom:semver 1.3.1-beta.6 + string public constant version = "1.3.1-beta.6"; /// @notice The starting timestamp of the game Timestamp public createdAt; diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol b/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol index 55b940c2d9dd..6db9cdeaada1 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IWETH } from "src/universal/interfaces/IWETH.sol"; +import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -interface IDelayedWETH is IWETH { +interface IDelayedWETH is IWETH98 { struct WithdrawalRequest { uint256 amount; uint256 timestamp; } + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event Initialized(uint8 version); event Unwrap(address indexed src, uint256 wad); fallback() external payable; @@ -25,8 +27,10 @@ interface IDelayedWETH is IWETH { function renounceOwnership() external; function unlock(address _guy, uint256 _wad) external; function withdraw(address _guy, uint256 _wad) external; - function withdrawals(address _owner, address _guy) external view returns (uint256, uint256); + function withdrawals(address, address) external view returns (uint256 amount, uint256 timestamp); function version() external view returns (string memory); + function withdraw(uint256 _wad) external override; + function __constructor__(uint256 _delay) external; } diff --git a/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol b/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol index 2508b34e43b3..b3b588258242 100644 --- a/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol +++ b/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol @@ -7,7 +7,7 @@ import { ILegacyMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; /// @title LegacyMintableERC20 /// @notice The legacy implementation of the OptimismMintableERC20. This /// contract is deprecated and should no longer be used. -contract LegacyMintableERC20 is ILegacyMintableERC20, ERC20 { +contract LegacyMintableERC20 is ERC20 { /// @notice Emitted when the token is minted by the bridge. event Mint(address indexed _account, uint256 _amount); diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol index 838a21af5662..34d601cbb65b 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol @@ -4,7 +4,8 @@ pragma solidity 0.8.15; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import { ILegacyMintableERC20, IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; @@ -14,7 +15,7 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; /// use an OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa. /// Designed to be backwards compatible with the older StandardL2ERC20 token which was only /// meant for use on L2. -contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ERC20Permit, ISemver { +contract OptimismMintableERC20 is ERC20Permit, ISemver { /// @notice Address of the corresponding version of this token on the remote chain. address public immutable REMOTE_TOKEN; @@ -41,8 +42,8 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, } /// @notice Semantic version. - /// @custom:semver 1.4.0-beta.1 - string public constant version = "1.4.0-beta.1"; + /// @custom:semver 1.4.0-beta.2 + string public constant version = "1.4.0-beta.2"; /// @notice Getter function for the permit2 address. It deterministically deployed /// so it will always be at the same address. It is also included as a preinstall, @@ -96,15 +97,7 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, /// @notice Allows the StandardBridge on this network to mint tokens. /// @param _to Address to mint tokens to. /// @param _amount Amount of tokens to mint. - function mint( - address _to, - uint256 _amount - ) - external - virtual - override(IOptimismMintableERC20, ILegacyMintableERC20) - onlyBridge - { + function mint(address _to, uint256 _amount) external virtual onlyBridge { _mint(_to, _amount); emit Mint(_to, _amount); } @@ -112,15 +105,7 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, /// @notice Allows the StandardBridge on this network to burn tokens. /// @param _from Address to burn tokens from. /// @param _amount Amount of tokens to burn. - function burn( - address _from, - uint256 _amount - ) - external - virtual - override(IOptimismMintableERC20, ILegacyMintableERC20) - onlyBridge - { + function burn(address _from, uint256 _amount) external virtual onlyBridge { _burn(_from, _amount); emit Burn(_from, _amount); } diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol index e179c6408dad..d7a9cd3372cd 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol @@ -48,8 +48,8 @@ contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20F /// the OptimismMintableERC20 token contract since this contract /// is responsible for deploying OptimismMintableERC20 contracts. /// @notice Semantic version. - /// @custom:semver 1.10.1-beta.3 - string public constant version = "1.10.1-beta.3"; + /// @custom:semver 1.10.1-beta.4 + string public constant version = "1.10.1-beta.4"; /// @notice Constructs the OptimismMintableERC20Factory contract. constructor() { diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol index 955a34072205..9dd05e10d1fe 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -12,14 +11,24 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @notice This contract is the remote representation for some token that lives on another network, /// typically an Optimism representation of an Ethereum-based token. Standard reference /// implementation that can be extended or modified according to your needs. -contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, ISemver { - /// @inheritdoc IOptimismMintableERC721 +contract OptimismMintableERC721 is ERC721Enumerable, ISemver { + /// @notice Emitted when a token is minted. + /// @param account Address of the account the token was minted to. + /// @param tokenId Token ID of the minted token. + event Mint(address indexed account, uint256 tokenId); + + /// @notice Emitted when a token is burned. + /// @param account Address of the account the token was burned from. + /// @param tokenId Token ID of the burned token. + event Burn(address indexed account, uint256 tokenId); + + /// @notice Chain ID of the chain where the remote token is deployed. uint256 public immutable REMOTE_CHAIN_ID; - /// @inheritdoc IOptimismMintableERC721 + /// @notice Address of the token on the remote domain. address public immutable REMOTE_TOKEN; - /// @inheritdoc IOptimismMintableERC721 + /// @notice Address of the ERC721 bridge on this network. address public immutable BRIDGE; /// @notice Base token URI for this token. @@ -32,8 +41,8 @@ contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, IS } /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.2 - string public constant version = "1.3.1-beta.2"; + /// @custom:semver 1.3.1-beta.3 + string public constant version = "1.3.1-beta.3"; /// @param _bridge Address of the bridge on this network. /// @param _remoteChainId Chain ID where the remote token is deployed. @@ -70,29 +79,34 @@ contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, IS ); } - /// @inheritdoc IOptimismMintableERC721 + /// @notice Chain ID of the chain where the remote token is deployed. function remoteChainId() external view returns (uint256) { return REMOTE_CHAIN_ID; } - /// @inheritdoc IOptimismMintableERC721 + /// @notice Address of the token on the remote domain. function remoteToken() external view returns (address) { return REMOTE_TOKEN; } - /// @inheritdoc IOptimismMintableERC721 + /// @notice Address of the ERC721 bridge on this network. function bridge() external view returns (address) { return BRIDGE; } - /// @inheritdoc IOptimismMintableERC721 + /// @notice Mints some token ID for a user, checking first that contract recipients + /// are aware of the ERC721 protocol to prevent tokens from being forever locked. + /// @param _to Address of the user to mint the token for. + /// @param _tokenId Token ID to mint. function safeMint(address _to, uint256 _tokenId) external virtual onlyBridge { _safeMint(_to, _tokenId); emit Mint(_to, _tokenId); } - /// @inheritdoc IOptimismMintableERC721 + /// @notice Burns a token ID from a user. + /// @param _from Address of the user to burn the token from. + /// @param _tokenId Token ID to burn. function burn(address _from, uint256 _tokenId) external virtual onlyBridge { _burn(_tokenId); @@ -102,7 +116,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, IS /// @notice Checks if a given interface ID is supported by this contract. /// @param _interfaceId The interface ID to check. /// @return True if the interface ID is supported, false otherwise. - function supportsInterface(bytes4 _interfaceId) public view override(ERC721Enumerable, IERC165) returns (bool) { + function supportsInterface(bytes4 _interfaceId) public view override(ERC721Enumerable) returns (bool) { bytes4 iface = type(IOptimismMintableERC721).interfaceId; return _interfaceId == iface || super.supportsInterface(_interfaceId); } diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol index 943859a7db1c..7350e0fae0de 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol @@ -25,8 +25,8 @@ contract OptimismMintableERC721Factory is ISemver { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); /// @notice Semantic version. - /// @custom:semver 1.4.1-beta.3 - string public constant version = "1.4.1-beta.3"; + /// @custom:semver 1.4.1-beta.4 + string public constant version = "1.4.1-beta.4"; /// @notice The semver MUST be bumped any time that there is a change in /// the OptimismMintableERC721 token contract since this contract diff --git a/packages/contracts-bedrock/src/universal/StandardBridge.sol b/packages/contracts-bedrock/src/universal/StandardBridge.sol index 476d3ba54c93..57af2247a65a 100644 --- a/packages/contracts-bedrock/src/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/StandardBridge.sol @@ -6,7 +6,8 @@ import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC16 import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; -import { IOptimismMintableERC20, ILegacyMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; diff --git a/packages/contracts-bedrock/src/universal/WETH98.sol b/packages/contracts-bedrock/src/universal/WETH98.sol index e211db22b473..c2909e6fa6f7 100644 --- a/packages/contracts-bedrock/src/universal/WETH98.sol +++ b/packages/contracts-bedrock/src/universal/WETH98.sol @@ -19,16 +19,38 @@ pragma solidity 0.8.15; -import { IWETH } from "src/universal/interfaces/IWETH.sol"; - /// @title WETH98 /// @notice WETH98 is a version of WETH9 upgraded for Solidity 0.8.x. -contract WETH98 is IWETH { +contract WETH98 { + /// @notice Returns the number of decimals the token uses. + /// @return The number of decimals the token uses. uint8 public constant decimals = 18; mapping(address => uint256) internal _balanceOf; mapping(address => mapping(address => uint256)) internal _allowance; + /// @notice Emitted when an approval is made. + /// @param src The address that approved the transfer. + /// @param guy The address that was approved to transfer. + /// @param wad The amount that was approved to transfer. + event Approval(address indexed src, address indexed guy, uint256 wad); + + /// @notice Emitted when a transfer is made. + /// @param src The address that transferred the WETH. + /// @param dst The address that received the WETH. + /// @param wad The amount of WETH that was transferred. + event Transfer(address indexed src, address indexed dst, uint256 wad); + + /// @notice Emitted when a deposit is made. + /// @param dst The address that deposited the WETH. + /// @param wad The amount of WETH that was deposited. + event Deposit(address indexed dst, uint256 wad); + + /// @notice Emitted when a withdrawal is made. + /// @param src The address that withdrew the WETH. + /// @param wad The amount of WETH that was withdrawn. + event Withdrawal(address indexed src, uint256 wad); + /// @notice Pipes to deposit. receive() external payable { deposit(); @@ -39,33 +61,41 @@ contract WETH98 is IWETH { deposit(); } - /// @inheritdoc IWETH - function name() external view virtual override returns (string memory) { + /// @notice Returns the name of the token. + /// @return name_ The name of the token. + function name() external view virtual returns (string memory) { return "Wrapped Ether"; } - /// @inheritdoc IWETH - function symbol() external view virtual override returns (string memory) { + /// @notice Returns the symbol of the token. + /// @return symbol_ The symbol of the token. + function symbol() external view virtual returns (string memory) { return "WETH"; } - /// @inheritdoc IWETH + /// @notice Returns the amount of WETH that the spender can transfer on behalf of the owner. + /// @param owner The address that owns the WETH. + /// @param spender The address that is approved to transfer the WETH. + /// @return The amount of WETH that the spender can transfer on behalf of the owner. function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowance[owner][spender]; } - /// @inheritdoc IWETH + /// @notice Returns the balance of the given address. + /// @param src The address to query the balance of. + /// @return The balance of the given address. function balanceOf(address src) public view returns (uint256) { return _balanceOf[src]; } - /// @inheritdoc IWETH + /// @notice Allows WETH to be deposited by sending ether to the contract. function deposit() public payable virtual { _balanceOf[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } - /// @inheritdoc IWETH + /// @notice Withdraws an amount of ETH. + /// @param wad The amount of ETH to withdraw. function withdraw(uint256 wad) public virtual { require(_balanceOf[msg.sender] >= wad); _balanceOf[msg.sender] -= wad; @@ -73,24 +103,35 @@ contract WETH98 is IWETH { emit Withdrawal(msg.sender, wad); } - /// @inheritdoc IWETH + /// @notice Returns the total supply of WETH. + /// @return The total supply of WETH. function totalSupply() external view returns (uint256) { return address(this).balance; } - /// @inheritdoc IWETH + /// @notice Approves the given address to transfer the WETH on behalf of the caller. + /// @param guy The address that is approved to transfer the WETH. + /// @param wad The amount that is approved to transfer. + /// @return True if the approval was successful. function approve(address guy, uint256 wad) external returns (bool) { _allowance[msg.sender][guy] = wad; emit Approval(msg.sender, guy, wad); return true; } - /// @inheritdoc IWETH + /// @notice Transfers the given amount of WETH to the given address. + /// @param dst The address to transfer the WETH to. + /// @param wad The amount of WETH to transfer. + /// @return True if the transfer was successful. function transfer(address dst, uint256 wad) external returns (bool) { return transferFrom(msg.sender, dst, wad); } - /// @inheritdoc IWETH + /// @notice Transfers the given amount of WETH from the given address to the given address. + /// @param src The address to transfer the WETH from. + /// @param dst The address to transfer the WETH to. + /// @param wad The amount of WETH to transfer. + /// @return True if the transfer was successful. function transferFrom(address src, address dst, uint256 wad) public returns (bool) { require(_balanceOf[src] >= wad); diff --git a/packages/contracts-bedrock/src/universal/interfaces/ILegacyMintableERC20.sol b/packages/contracts-bedrock/src/universal/interfaces/ILegacyMintableERC20.sol new file mode 100644 index 000000000000..ad518b8b4d77 --- /dev/null +++ b/packages/contracts-bedrock/src/universal/interfaces/ILegacyMintableERC20.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +/// @custom:legacy +/// @title ILegacyMintableERC20 +/// @notice This interface was available on the legacy L2StandardERC20 contract. +/// It remains available on the OptimismMintableERC20 contract for +/// backwards compatibility. + +interface ILegacyMintableERC20 is IERC165 { + function l1Token() external view returns (address); + + function mint(address _to, uint256 _amount) external; + + function burn(address _from, uint256 _amount) external; +} diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20.sol index b261902c72ee..caf54b807314 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20.sol @@ -16,16 +16,3 @@ interface IOptimismMintableERC20 is IERC165 { function burn(address _from, uint256 _amount) external; } - -/// @custom:legacy -/// @title ILegacyMintableERC20 -/// @notice This interface was available on the legacy L2StandardERC20 contract. -/// It remains available on the OptimismMintableERC20 contract for -/// backwards compatibility. -interface ILegacyMintableERC20 is IERC165 { - function l1Token() external view returns (address); - - function mint(address _to, uint256 _amount) external; - - function burn(address _from, uint256 _amount) external; -} diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol index 0f9133d7dc67..7d745ad8436e 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol @@ -1,48 +1,74 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; - /// @title IOptimismMintableERC721 /// @notice Interface for contracts that are compatible with the OptimismMintableERC721 standard. /// Tokens that follow this standard can be easily transferred across the ERC721 bridge. -interface IOptimismMintableERC721 is IERC721Enumerable { - /// @notice Emitted when a token is minted. - /// @param account Address of the account the token was minted to. - /// @param tokenId Token ID of the minted token. - event Mint(address indexed account, uint256 tokenId); +interface IOptimismMintableERC721 { + function __constructor__( + address _bridge, + uint256 _remoteChainId, + address _remoteToken, + string memory _name, + string memory _symbol + ) + external; - /// @notice Emitted when a token is burned. - /// @param account Address of the account the token was burned from. - /// @param tokenId Token ID of the burned token. + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event Burn(address indexed account, uint256 tokenId); + event Mint(address indexed account, uint256 tokenId); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + function totalSupply() external view returns (uint256); + + function approve(address to, uint256 tokenId) external; + + function isApprovedForAll(address owner, address operator) external view returns (bool); + + function symbol() external view returns (string memory); + + function tokenByIndex(uint256 index) external view returns (uint256); + + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + function transferFrom(address from, address to, uint256 tokenId) external; + + function balanceOf(address owner) external view returns (uint256); + + function baseTokenURI() external view returns (string memory); + + function getApproved(uint256 tokenId) external view returns (address); + + function name() external view returns (string memory); + + function ownerOf(uint256 tokenId) external view returns (address); + + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) external; + + function setApprovalForAll(address operator, bool approved) external; + + function supportsInterface(bytes4 _interfaceId) external view returns (bool); + + function tokenURI(uint256 tokenId) external view returns (string memory); + + function version() external view returns (string memory); - /// @notice Mints some token ID for a user, checking first that contract recipients - /// are aware of the ERC721 protocol to prevent tokens from being forever locked. - /// @param _to Address of the user to mint the token for. - /// @param _tokenId Token ID to mint. function safeMint(address _to, uint256 _tokenId) external; - /// @notice Burns a token ID from a user. - /// @param _from Address of the user to burn the token from. - /// @param _tokenId Token ID to burn. function burn(address _from, uint256 _tokenId) external; - /// @notice Chain ID of the chain where the remote token is deployed. function REMOTE_CHAIN_ID() external view returns (uint256); - /// @notice Address of the token on the remote domain. function REMOTE_TOKEN() external view returns (address); - /// @notice Address of the ERC721 bridge on this network. function BRIDGE() external view returns (address); - /// @notice Chain ID of the chain where the remote token is deployed. function remoteChainId() external view returns (uint256); - /// @notice Address of the token on the remote domain. function remoteToken() external view returns (address); - /// @notice Address of the ERC721 bridge on this network. function bridge() external view returns (address); } diff --git a/packages/contracts-bedrock/src/universal/interfaces/IWETH.sol b/packages/contracts-bedrock/src/universal/interfaces/IWETH.sol deleted file mode 100644 index 73ef0e954577..000000000000 --- a/packages/contracts-bedrock/src/universal/interfaces/IWETH.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/// @title IWETH -/// @notice Interface for WETH9. -interface IWETH { - /// @notice Emitted when an approval is made. - /// @param src The address that approved the transfer. - /// @param guy The address that was approved to transfer. - /// @param wad The amount that was approved to transfer. - event Approval(address indexed src, address indexed guy, uint256 wad); - - /// @notice Emitted when a transfer is made. - /// @param src The address that transferred the WETH. - /// @param dst The address that received the WETH. - /// @param wad The amount of WETH that was transferred. - event Transfer(address indexed src, address indexed dst, uint256 wad); - - /// @notice Emitted when a deposit is made. - /// @param dst The address that deposited the WETH. - /// @param wad The amount of WETH that was deposited. - event Deposit(address indexed dst, uint256 wad); - - /// @notice Emitted when a withdrawal is made. - /// @param src The address that withdrew the WETH. - /// @param wad The amount of WETH that was withdrawn. - event Withdrawal(address indexed src, uint256 wad); - - /// @notice Returns the name of the token. - /// @return The name of the token. - function name() external view returns (string memory); - - /// @notice Returns the symbol of the token. - /// @return The symbol of the token. - function symbol() external view returns (string memory); - - /// @notice Returns the number of decimals the token uses. - /// @return The number of decimals the token uses. - function decimals() external pure returns (uint8); - - /// @notice Returns the balance of the given address. - /// @param owner The address to query the balance of. - /// @return The balance of the given address. - function balanceOf(address owner) external view returns (uint256); - - /// @notice Returns the amount of WETH that the spender can transfer on behalf of the owner. - /// @param owner The address that owns the WETH. - /// @param spender The address that is approved to transfer the WETH. - /// @return The amount of WETH that the spender can transfer on behalf of the owner. - function allowance(address owner, address spender) external view returns (uint256); - - /// @notice Allows WETH to be deposited by sending ether to the contract. - function deposit() external payable; - - /// @notice Withdraws an amount of ETH. - /// @param wad The amount of ETH to withdraw. - function withdraw(uint256 wad) external; - - /// @notice Returns the total supply of WETH. - /// @return The total supply of WETH. - function totalSupply() external view returns (uint256); - - /// @notice Approves the given address to transfer the WETH on behalf of the caller. - /// @param guy The address that is approved to transfer the WETH. - /// @param wad The amount that is approved to transfer. - /// @return True if the approval was successful. - function approve(address guy, uint256 wad) external returns (bool); - - /// @notice Transfers the given amount of WETH to the given address. - /// @param dst The address to transfer the WETH to. - /// @param wad The amount of WETH to transfer. - /// @return True if the transfer was successful. - function transfer(address dst, uint256 wad) external returns (bool); - - /// @notice Transfers the given amount of WETH from the given address to the given address. - /// @param src The address to transfer the WETH from. - /// @param dst The address to transfer the WETH to. - /// @param wad The amount of WETH to transfer. - /// @return True if the transfer was successful. - function transferFrom(address src, address dst, uint256 wad) external returns (bool); -} diff --git a/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol b/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol new file mode 100644 index 000000000000..87f8844ae584 --- /dev/null +++ b/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title IWETH +/// @notice Interface for WETH9. +interface IWETH98 { + receive() external payable; + fallback() external payable; + + event Approval(address indexed src, address indexed guy, uint256 wad); + + event Transfer(address indexed src, address indexed dst, uint256 wad); + + event Deposit(address indexed dst, uint256 wad); + + event Withdrawal(address indexed src, uint256 wad); + + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); + + function balanceOf(address src) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + function deposit() external payable; + + function withdraw(uint256 wad) external; + + function totalSupply() external view returns (uint256); + + function approve(address guy, uint256 wad) external returns (bool); + + function transfer(address dst, uint256 wad) external returns (bool); + + function transferFrom(address src, address dst, uint256 wad) external returns (bool); +} diff --git a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol index 8078e2c01c74..643b684e07d9 100644 --- a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol @@ -11,6 +11,7 @@ import { TransientContext } from "src/libraries/TransientContext.sol"; // Target contracts import { CrossL2Inbox, + Identifier, NotEntered, NoExecutingDeposits, InvalidTimestamp, @@ -20,7 +21,6 @@ import { InteropStartAlreadySet } from "src/L2/CrossL2Inbox.sol"; import { IL1BlockInterop } from "src/L2/interfaces/IL1BlockInterop.sol"; -import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol"; /// @title CrossL2InboxWithModifiableTransientStorage /// @dev CrossL2Inbox contract with methods to modify the transient storage. @@ -141,7 +141,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function succeeds. function testFuzz_executeMessage_succeeds( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, address _target, bytes calldata _message, uint256 _value @@ -200,14 +200,14 @@ contract CrossL2InboxTest is Test { /// @dev Mock reentrant function that calls the `executeMessage` function. /// @param _id Identifier to pass to the `executeMessage` function. - function mockReentrant(ICrossL2Inbox.Identifier calldata _id) external payable { + function mockReentrant(Identifier calldata _id) external payable { crossL2Inbox.executeMessage({ _id: _id, _target: address(0), _message: "" }); } /// @dev Tests that the `executeMessage` function successfully handles reentrant calls. function testFuzz_executeMessage_reentrant_succeeds( - ICrossL2Inbox.Identifier memory _id1, // identifier passed to `executeMessage` by the initial call. - ICrossL2Inbox.Identifier memory _id2, // identifier passed to `executeMessage` by the reentrant call. + Identifier memory _id1, // identifier passed to `executeMessage` by the initial call. + Identifier memory _id2, // identifier passed to `executeMessage` by the reentrant call. uint256 _value ) external @@ -272,7 +272,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts if the transaction comes from a deposit. function testFuzz_executeMessage_isDeposit_reverts( - ICrossL2Inbox.Identifier calldata _id, + Identifier calldata _id, address _target, bytes calldata _message, uint256 _value @@ -298,7 +298,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts when called with an identifier with an invalid timestamp. function testFuzz_executeMessage_invalidTimestamp_reverts( - ICrossL2Inbox.Identifier calldata _id, + Identifier calldata _id, address _target, bytes calldata _message, uint256 _value @@ -329,7 +329,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts when called with an identifier with a timestamp earlier /// than INTEROP_START timestamp function testFuzz_executeMessage_invalidTimestamp_interopStart_reverts( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, address _target, bytes calldata _message, uint256 _value @@ -360,7 +360,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts when called with an identifier with a chain ID not in /// dependency set. function testFuzz_executeMessage_invalidChainId_reverts( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, address _target, bytes calldata _message, uint256 _value @@ -398,7 +398,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts when the target call fails. function testFuzz_executeMessage_targetCallFailed_reverts( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, address _target, bytes calldata _message, uint256 _value @@ -443,13 +443,7 @@ contract CrossL2InboxTest is Test { crossL2Inbox.executeMessage{ value: _value }({ _id: _id, _target: _target, _message: _message }); } - function testFuzz_validateMessage_succeeds( - ICrossL2Inbox.Identifier memory _id, - bytes32 _messageHash - ) - external - setInteropStart - { + function testFuzz_validateMessage_succeeds(Identifier memory _id, bytes32 _messageHash) external setInteropStart { // Ensure that the id's timestamp is valid (less than or equal to the current block timestamp and greater than // interop start time) _id.timestamp = bound(_id.timestamp, interopStartTime + 1, block.timestamp); @@ -476,12 +470,7 @@ contract CrossL2InboxTest is Test { crossL2Inbox.validateMessage(_id, _messageHash); } - function testFuzz_validateMessage_isDeposit_reverts( - ICrossL2Inbox.Identifier calldata _id, - bytes32 _messageHash - ) - external - { + function testFuzz_validateMessage_isDeposit_reverts(Identifier calldata _id, bytes32 _messageHash) external { // Ensure it is a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, @@ -499,7 +488,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `validateMessage` function reverts when called with an identifier with a timestamp later /// than current block.timestamp. function testFuzz_validateMessage_invalidTimestamp_reverts( - ICrossL2Inbox.Identifier calldata _id, + Identifier calldata _id, bytes32 _messageHash ) external @@ -525,7 +514,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `validateMessage` function reverts when called with an identifier with a timestamp earlier /// than INTEROP_START timestamp function testFuzz_validateMessage_invalidTimestamp_interopStart_reverts( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, bytes32 _messageHash ) external @@ -551,7 +540,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `validateMessage` function reverts when called with an identifier with a chain ID not in the /// dependency set. function testFuzz_validateMessage_invalidChainId_reverts( - ICrossL2Inbox.Identifier memory _id, + Identifier memory _id, bytes32 _messageHash ) external diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index 36b36973af8d..2c413bcfbe89 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -10,8 +10,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; import { Hashing } from "src/libraries/Hashing.sol"; // Target contract -import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol"; -import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol"; +import { CrossL2Inbox, Identifier } from "src/L2/CrossL2Inbox.sol"; import { L2ToL2CrossDomainMessenger, NotEntered, @@ -218,8 +217,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) }); // Construct the SentMessage payload & identifier - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics abi.encode(_sender, _message) // data @@ -264,8 +263,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { vm.expectRevert(EventPayloadNotSentMessage.selector); // Point to a different remote log that the inbox validates - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encode(L2ToL2CrossDomainMessenger.RelayedMessage.selector, _source, _nonce, _msgHash); @@ -325,8 +324,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { vm.expectCall({ callee: target, msgValue: _value, data: message }); // Construct and relay the message - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics abi.encode(_sender, message) // data @@ -368,8 +367,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { vm.expectRevert(ReentrantCall.selector); - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, 1, 1, 1, _source); + Identifier memory id = Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, 1, 1, 1, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, address(0), _nonce), // topics abi.encode(_sender, "") // data @@ -407,8 +405,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { vm.expectCall({ callee: target, msgValue: _value, data: message }); // Construct and relay the message - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source1); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source1); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics abi.encode(_sender1, message) // data @@ -457,7 +455,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Expect a revert with the IdOriginNotL2ToL2CrossDomainMessenger vm.expectRevert(IdOriginNotL2ToL2CrossDomainMessenger.selector); - ICrossL2Inbox.Identifier memory id = ICrossL2Inbox.Identifier(_origin, _blockNum, _logIndex, _time, _source); + Identifier memory id = Identifier(_origin, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics abi.encode(_sender, _message) // data @@ -489,8 +487,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Expect a revert with the MessageDestinationNotRelayChain selector vm.expectRevert(MessageDestinationNotRelayChain.selector); - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, _destination, _target, _nonce), // topics abi.encode(_sender, _message) // data @@ -526,8 +524,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Call `relayMessage` with CrossL2Inbox as the target to provoke revert. The current chain is the destination // to prevent revert due to invalid destination - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode( L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, Predeploys.CROSS_L2_INBOX, _nonce @@ -565,8 +563,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Call `relayMessage` with L2ToL2CrossDomainMessenger as the target to provoke revert. The current chain is the // destination to prevent revert due to invalid destination - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode( L2ToL2CrossDomainMessenger.SentMessage.selector, @@ -620,8 +618,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { _source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)) ); - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics abi.encode(_sender, _message) // data @@ -670,8 +668,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure that the target contract reverts vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) }); - ICrossL2Inbox.Identifier memory id = - ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); + Identifier memory id = + Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics abi.encode(_sender, _message) // data diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index 10ee252218fc..ef2b654b2410 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -45,7 +45,7 @@ import { IGasPriceOracle } from "src/L2/interfaces/IGasPriceOracle.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; -import { IWETH } from "src/universal/interfaces/IWETH.sol"; +import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; import { IGovernanceToken } from "src/governance/interfaces/IGovernanceToken.sol"; import { ILegacyMessagePasser } from "src/legacy/interfaces/ILegacyMessagePasser.sol"; import { ISuperchainTokenBridge } from "src/L2/interfaces/ISuperchainTokenBridge.sol"; @@ -105,7 +105,7 @@ contract Setup { IL1Block l1Block = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES); IGovernanceToken governanceToken = IGovernanceToken(Predeploys.GOVERNANCE_TOKEN); ILegacyMessagePasser legacyMessagePasser = ILegacyMessagePasser(Predeploys.LEGACY_MESSAGE_PASSER); - IWETH weth = IWETH(payable(Predeploys.WETH)); + IWETH98 weth = IWETH98(payable(Predeploys.WETH)); ISuperchainWETH superchainWeth = ISuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH)); IETHLiquidity ethLiquidity = IETHLiquidity(Predeploys.ETH_LIQUIDITY); ISuperchainTokenBridge superchainTokenBridge = ISuperchainTokenBridge(Predeploys.SUPERCHAIN_TOKEN_BRIDGE); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol index 1e84ce2958cf..19b156e2f6e6 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol @@ -2,7 +2,8 @@ pragma solidity 0.8.15; import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; -import { ILegacyMintableERC20, IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; +import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract OptimismMintableERC20_Test is Bridge_Initializer { diff --git a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol index e2f62b32b843..be7f8a51107c 100644 --- a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol @@ -30,7 +30,7 @@ contract StandardBridgeTester is StandardBridge { /// @title LegacyMintable /// @notice Simple implementation of the legacy OptimismMintableERC20. -contract LegacyMintable is ERC20, ILegacyMintableERC20 { +contract LegacyMintable is ERC20 { constructor(string memory _name, string memory _ticker) ERC20(_name, _ticker) { } function l1Token() external pure returns (address) { diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 08cc5f74685d..9456de1ff920 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -46,6 +46,8 @@ rules: pattern-regex: function\s+\w+\s*\(\s*([^)]*?\b\w+\s+(?!_)(?!memory\b)(?!calldata\b)(?!storage\b)(?!payable\b)\w+\s*(?=,|\))) paths: exclude: + - packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol + - packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol - op-chain-ops/script/testdata/scripts/ScriptExample.s.sol - packages/contracts-bedrock/test - packages/contracts-bedrock/scripts/libraries/Solarray.sol @@ -64,6 +66,7 @@ rules: pattern-regex: returns\s*(\w+\s*)?\(\s*([^)]*?\b\w+\s+(?!memory\b)(?!calldata\b)(?!storage\b)(?!payable\b)\w+(? Date: Fri, 25 Oct 2024 21:17:38 -0700 Subject: [PATCH 035/451] dependabot(gomod): bump github.com/minio/minio-go/v7 (#12654) Bumps [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) from 7.0.78 to 7.0.79. - [Release notes](https://github.com/minio/minio-go/releases) - [Commits](https://github.com/minio/minio-go/compare/v7.0.78...v7.0.79) --- updated-dependencies: - dependency-name: github.com/minio/minio-go/v7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ab9e5725ce87..758d076028e8 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/mattn/go-isatty v0.0.20 - github.com/minio/minio-go/v7 v7.0.78 + github.com/minio/minio-go/v7 v7.0.79 github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-multiaddr v0.13.0 github.com/multiformats/go-multiaddr-dns v0.4.0 diff --git a/go.sum b/go.sum index de095722c8b5..a2dc3af16462 100644 --- a/go.sum +++ b/go.sum @@ -524,8 +524,8 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4S github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.78 h1:LqW2zy52fxnI4gg8C2oZviTaKHcBV36scS+RzJnxUFs= -github.com/minio/minio-go/v7 v7.0.78/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/minio-go/v7 v7.0.79 h1:SvJZpj3hT0RN+4KiuX/FxLfPZdsuegy6d/2PiemM/bM= +github.com/minio/minio-go/v7 v7.0.79/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= From 6d57ace1a5102b70fb874d111d739757a3e7aeae Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Sat, 26 Oct 2024 11:19:49 +0700 Subject: [PATCH 036/451] maint(ct): replace natspec semver check with semgrep (#12625) Replaces the golang package for natspec/semver matching with a semgrep rule. Code reduction is good. --- .circleci/config.yml | 3 - packages/contracts-bedrock/justfile | 9 - .../scripts/checks/semver-natspec/main.go | 220 ------------------ .../checks/semver-natspec/main_test.go | 124 ---------- semgrep/sol-rules.t.sol | 34 +++ semgrep/sol-rules.yaml | 26 +++ 6 files changed, 60 insertions(+), 356 deletions(-) delete mode 100644 packages/contracts-bedrock/scripts/checks/semver-natspec/main.go delete mode 100644 packages/contracts-bedrock/scripts/checks/semver-natspec/main_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 648217dbcdf8..1a8539db65c6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -684,8 +684,6 @@ jobs: command: semver-lock - run-contracts-check: command: semver-diff-check-no-build - - run-contracts-check: - command: semver-natspec-check-no-build - run-contracts-check: command: validate-deploy-configs - run-contracts-check: @@ -864,7 +862,6 @@ jobs: op-service op-supervisor op-deployer - packages/contracts-bedrock/scripts/checks/semver-natspec ) formatted_packages="" for package in "${packages[@]}"; do diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 897114cdc11a..11bd9ed4946b 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -155,14 +155,6 @@ semver-diff-check-no-build: # Checks that any contracts with a modified semver lock also have a modified semver version. semver-diff-check: build semver-diff-check-no-build -# Checks that semver natspec is equal to the actual semver version. -# Does not build contracts. -semver-natspec-check-no-build: - go run ./scripts/checks/semver-natspec - -# Checks that semver natspec is equal to the actual semver version. -semver-natspec-check: build semver-natspec-check-no-build - # Checks that the semgrep tests are valid. semgrep-test-validity-check: forge fmt ../../semgrep/sol-rules.t.sol --check @@ -206,7 +198,6 @@ check: snapshots-check-no-build \ lint-check \ semver-diff-check-no-build \ - semver-natspec-check-no-build \ validate-deploy-configs \ validate-spacers-no-build \ interfaces-check-no-build diff --git a/packages/contracts-bedrock/scripts/checks/semver-natspec/main.go b/packages/contracts-bedrock/scripts/checks/semver-natspec/main.go deleted file mode 100644 index 1be082dda763..000000000000 --- a/packages/contracts-bedrock/scripts/checks/semver-natspec/main.go +++ /dev/null @@ -1,220 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "runtime" - "strings" - "sync" - "sync/atomic" -) - -type ArtifactsWrapper struct { - RawMetadata string `json:"rawMetadata"` -} - -type Artifacts struct { - Output struct { - Devdoc struct { - StateVariables struct { - Version struct { - Semver string `json:"custom:semver"` - } `json:"version"` - } `json:"stateVariables,omitempty"` - Methods struct { - Version struct { - Semver string `json:"custom:semver"` - } `json:"version()"` - } `json:"methods,omitempty"` - } `json:"devdoc"` - } `json:"output"` -} - -var ConstantVersionPattern = regexp.MustCompile(`string.*constant.*version\s+=\s+"([^"]+)";`) - -var FunctionVersionPattern = regexp.MustCompile(`^\s+return\s+"((?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)";$`) - -var InteropVersionPattern = regexp.MustCompile(`^\s+return\s+string\.concat\(super\.version\(\), "((.*)\+interop(.*)?)"\);`) - -func main() { - if err := run(); err != nil { - writeStderr("an error occurred: %v", err) - os.Exit(1) - } -} - -func writeStderr(msg string, args ...any) { - _, _ = fmt.Fprintf(os.Stderr, msg+"\n", args...) -} - -func run() error { - cwd, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get current working directory: %w", err) - } - - writeStderr("working directory: %s", cwd) - - artifactsDir := filepath.Join(cwd, "forge-artifacts") - srcDir := filepath.Join(cwd, "src") - - artifactFiles, err := glob(artifactsDir, ".json") - if err != nil { - return fmt.Errorf("failed to get artifact files: %w", err) - } - contractFiles, err := glob(srcDir, ".sol") - if err != nil { - return fmt.Errorf("failed to get contract files: %w", err) - } - - var hasErr int32 - var outMtx sync.Mutex - fail := func(msg string, args ...any) { - outMtx.Lock() - writeStderr("❌ "+msg, args...) - outMtx.Unlock() - atomic.StoreInt32(&hasErr, 1) - } - - sem := make(chan struct{}, runtime.NumCPU()) - for contractName, artifactPath := range artifactFiles { - contractName := contractName - artifactPath := artifactPath - - sem <- struct{}{} - - go func() { - defer func() { - <-sem - }() - - af, err := os.Open(artifactPath) - if err != nil { - fail("%s: failed to open contract artifact: %v", contractName, err) - return - } - defer af.Close() - - var wrapper ArtifactsWrapper - if err := json.NewDecoder(af).Decode(&wrapper); err != nil { - fail("%s: failed to parse artifact file: %v", contractName, err) - return - } - - if wrapper.RawMetadata == "" { - return - } - - var artifactData Artifacts - if err := json.Unmarshal([]byte(wrapper.RawMetadata), &artifactData); err != nil { - fail("%s: failed to unwrap artifact metadata: %v", contractName, err) - return - } - - artifactVersion := artifactData.Output.Devdoc.StateVariables.Version.Semver - - isConstant := true - if artifactData.Output.Devdoc.StateVariables.Version.Semver == "" { - artifactVersion = artifactData.Output.Devdoc.Methods.Version.Semver - isConstant = false - } - - if artifactVersion == "" { - return - } - - // Skip mock contracts - if strings.HasPrefix(contractName, "Mock") { - return - } - - contractPath := contractFiles[contractName] - if contractPath == "" { - fail("%s: Source file not found (For test mock contracts, prefix the name with 'Mock' to ignore this warning)", contractName) - return - } - - cf, err := os.Open(contractPath) - if err != nil { - fail("%s: failed to open contract source: %v", contractName, err) - return - } - defer cf.Close() - - sourceData, err := io.ReadAll(cf) - if err != nil { - fail("%s: failed to read contract source: %v", contractName, err) - return - } - - var sourceVersion string - - if isConstant { - sourceVersion = findLine(sourceData, ConstantVersionPattern) - } else { - sourceVersion = findLine(sourceData, FunctionVersionPattern) - } - - // Need to define a special case for interop contracts since they technically - // use an invalid semver format. Checking for sourceVersion == "" allows the - // team to update the format to a valid semver format in the future without - // needing to change this program. - if sourceVersion == "" && strings.HasSuffix(contractName, "Interop") { - sourceVersion = findLine(sourceData, InteropVersionPattern) - } - - if sourceVersion == "" { - fail("%s: version not found in source", contractName) - return - } - - if sourceVersion != artifactVersion { - fail("%s: version mismatch: source=%s, artifact=%s", contractName, sourceVersion, artifactVersion) - return - } - - _, _ = fmt.Fprintf(os.Stderr, "✅ %s: code: %s, artifact: %s\n", contractName, sourceVersion, artifactVersion) - }() - } - - for i := 0; i < cap(sem); i++ { - sem <- struct{}{} - } - - if atomic.LoadInt32(&hasErr) == 1 { - return fmt.Errorf("semver check failed, see logs above") - } - - return nil -} - -func glob(dir string, ext string) (map[string]string, error) { - out := make(map[string]string) - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if !info.IsDir() && filepath.Ext(path) == ext { - out[strings.TrimSuffix(filepath.Base(path), ext)] = path - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to walk directory: %w", err) - } - return out, nil -} - -func findLine(in []byte, pattern *regexp.Regexp) string { - scanner := bufio.NewScanner(bytes.NewReader(in)) - for scanner.Scan() { - match := pattern.FindStringSubmatch(scanner.Text()) - if len(match) > 0 { - return match[1] - } - } - return "" -} diff --git a/packages/contracts-bedrock/scripts/checks/semver-natspec/main_test.go b/packages/contracts-bedrock/scripts/checks/semver-natspec/main_test.go deleted file mode 100644 index 7a8872d76d78..000000000000 --- a/packages/contracts-bedrock/scripts/checks/semver-natspec/main_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package main - -import ( - "regexp" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRegexes(t *testing.T) { - t.Run("ConstantVersionPattern", func(t *testing.T) { - testRegex(t, ConstantVersionPattern, []regexTest{ - { - name: "constant version", - input: `string constant version = "1.2.3";`, - capture: "1.2.3", - }, - { - name: "constant version with weird spaces", - input: ` string constant version = "1.2.3";`, - capture: "1.2.3", - }, - { - name: "constant version with visibility", - input: `string public constant version = "1.2.3";`, - capture: "1.2.3", - }, - { - name: "different variable name", - input: `string constant VERSION = "1.2.3";`, - capture: "", - }, - { - name: "different type", - input: `uint constant version = 1;`, - capture: "", - }, - { - name: "not constant", - input: `string version = "1.2.3";`, - capture: "", - }, - { - name: "unterminated", - input: `string constant version = "1.2.3"`, - capture: "", - }, - }) - }) - - t.Run("FunctionVersionPattern", func(t *testing.T) { - testRegex(t, FunctionVersionPattern, []regexTest{ - { - name: "function version", - input: ` return "1.2.3";`, - capture: "1.2.3", - }, - { - name: "function version with weird spaces", - input: ` return "1.2.3";`, - capture: "1.2.3", - }, - { - name: "function version with prerelease", - input: ` return "1.2.3-alpha.1";`, - capture: "1.2.3-alpha.1", - }, - { - name: "invalid semver", - input: ` return "1.2.cabdab";`, - capture: "", - }, - { - name: "not a return statement", - input: `function foo()`, - capture: "", - }, - }) - }) - - t.Run("InteropVersionPattern", func(t *testing.T) { - testRegex(t, InteropVersionPattern, []regexTest{ - { - name: "interop version", - input: ` return string.concat(super.version(), "+interop");`, - capture: "+interop", - }, - { - name: "interop version but as a valid semver", - input: ` return string.concat(super.version(), "0.0.0+interop");`, - capture: "0.0.0+interop", - }, - { - name: "not an interop version", - input: ` return string.concat(super.version(), "hello!");`, - capture: "", - }, - { - name: "invalid syntax", - input: ` return string.concat(super.version(), "0.0.0+interop`, - capture: "", - }, - { - name: "something else is concatted", - input: ` return string.concat("superduper", "mart");`, - capture: "", - }, - }) - }) -} - -type regexTest struct { - name string - input string - capture string -} - -func testRegex(t *testing.T, re *regexp.Regexp, tests []regexTest) { - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - require.Equal(t, test.capture, findLine([]byte(test.input), re)) - }) - } -} diff --git a/semgrep/sol-rules.t.sol b/semgrep/sol-rules.t.sol index b697d94e934c..b2b769e144e6 100644 --- a/semgrep/sol-rules.t.sol +++ b/semgrep/sol-rules.t.sol @@ -120,6 +120,40 @@ contract SemgrepTest__sol_safety_expectrevert_no_args { } } +contract SemgrepTest__sol_safety_natspec_semver_match { + // ok: sol-safety-natspec-semver-match + /// @custom:semver 2.8.1-beta.4 + string public constant version = "2.8.1-beta.4"; + + // ok: sol-safety-natspec-semver-match + /// @custom:semver 2.8.1-beta.3 + function version() public pure virtual returns (string memory) { + return "2.8.1-beta.3"; + } + + // ok: sol-safety-natspec-semver-match + /// @custom:semver +interop-beta.1 + function version() public pure override returns (string memory) { + return string.concat(super.version(), "+interop-beta.1"); + } + + // ruleid: sol-safety-natspec-semver-match + /// @custom:semver 2.8.1-beta.5 + string public constant version = "2.8.1-beta.4"; + + // ruleid: sol-safety-natspec-semver-match + /// @custom:semver 2.8.1-beta.4 + function version() public pure virtual returns (string memory) { + return "2.8.1-beta.3"; + } + + // ruleid: sol-safety-natspec-semver-match + /// @custom:semver +interop-beta.2 + function version() public pure override returns (string memory) { + return string.concat(super.version(), "+interop-beta.1"); + } +} + contract SemgrepTest__sol_style_input_arg_fmt { // ok: sol-style-input-arg-fmt event Test(address indexed src, address indexed guy, uint256 wad); diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 9456de1ff920..5646182a04f2 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -39,6 +39,32 @@ rules: exclude: - packages/contracts-bedrock/test/dispute/WETH98.t.sol + - id: sol-safety-natspec-semver-match + languages: [generic] + severity: ERROR + message: Semgrep defined in contract must match natspec $VERSION1 $VERSION2 + patterns: + - pattern-either: + - pattern-regex: /// @custom:semver + (?P[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.]+)?)\s+string + public constant version = + "(?P[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.]+)?)"; + - pattern-regex: /// @custom:semver + (?P[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.]+)?)\s+function + version\(\) public pure virtual returns \(string memory\) + \{\s+return + "(?P[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.]+)?)"; + - pattern-regex: /// @custom:semver (?P[a-zA-Z0-9.+-]+)\s+function + version\(\) public pure override returns \(string memory\) + \{\s+return string\.concat\(super\.version\(\), + "(?P[a-zA-Z0-9.+-]+)"\); + - metavariable-comparison: + comparison: $VERSION1 != $VERSION2 + metavariable: $VERSION1 + paths: + include: + - packages/contracts-bedrock/src + - id: sol-style-input-arg-fmt languages: [solidity] severity: ERROR From 96468b797644a794243b81f06f9c5b9c8169967e Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Fri, 25 Oct 2024 22:20:00 -0600 Subject: [PATCH 037/451] op-deployer: Add Docker builds (#12660) * op-deployer: Add Docker builds * fix job syntax * specify dockerfile * remove accidental whitespace changes --- .circleci/config.yml | 20 ++++++++++++++++++-- op-deployer/.goreleaser.yaml | 14 ++++++++++++++ op-deployer/Dockerfile.default | 3 +++ op-deployer/Dockerfile.minimal | 3 +++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 op-deployer/Dockerfile.default create mode 100644 op-deployer/Dockerfile.minimal diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a8539db65c6..a041bedb9ecb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1242,9 +1242,25 @@ jobs: description: Goreleaser config file default: .goreleaser.yaml type: string - machine: true - resource_class: ethereum-optimism/latitude-1 + docker: + - image: <> + resource_class: large steps: + - setup_remote_docker + - gcp-cli/install + - gcp-oidc-authenticate + - run: + name: Install goreleaser pro + command: | + mkdir goreleaser + curl -L -o goreleaser.tgz https://github.com/goreleaser/goreleaser-pro/releases/download/v2.3.2-pro/goreleaser-pro_Linux_x86_64.tar.gz + tar -xzvf goreleaser.tgz + mv goreleaser /usr/local/bin/goreleaser + rm -rf goreleaser + - run: + name: Configure Docker + command: | + gcloud auth configure-docker us-docker.pkg.dev - checkout - run: name: Run goreleaser diff --git a/op-deployer/.goreleaser.yaml b/op-deployer/.goreleaser.yaml index a17f171783b4..0104574ec865 100644 --- a/op-deployer/.goreleaser.yaml +++ b/op-deployer/.goreleaser.yaml @@ -50,6 +50,20 @@ archives: - goos: windows format: zip +dockers: + - id: default + goos: linux + goarch: amd64 + dockerfile: Dockerfile.default + image_templates: + - "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-deployer:{{ .Tag }}" + - id: minimal + goos: linux + goarch: amd64 + dockerfile: Dockerfile.minimal + image_templates: + - "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-deployer:{{ .Tag }}-minimal" + changelog: sort: asc filters: diff --git a/op-deployer/Dockerfile.default b/op-deployer/Dockerfile.default new file mode 100644 index 000000000000..cc5ca8d95e4f --- /dev/null +++ b/op-deployer/Dockerfile.default @@ -0,0 +1,3 @@ +FROM debian:bookworm-20240812-slim +ENTRYPOINT ["/op-deployer"] +COPY op-deployer /op-deployer \ No newline at end of file diff --git a/op-deployer/Dockerfile.minimal b/op-deployer/Dockerfile.minimal new file mode 100644 index 000000000000..9e8811980090 --- /dev/null +++ b/op-deployer/Dockerfile.minimal @@ -0,0 +1,3 @@ +FROM scratch +ENTRYPOINT ["/op-deployer"] +COPY op-deployer /op-deployer \ No newline at end of file From 7873831f805508c8d4d418a80a1bde5ad79314a3 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Sat, 26 Oct 2024 06:18:41 +0100 Subject: [PATCH 038/451] Sc/use deployutils in tests (#12656) * use deploy utils over new* in tests * replace new * with use of deployUtils * fix failing test * use deployutils for weth98 test file * fix semgrep * fixes * fixes * fixes... --- .../scripts/libraries/DeployUtils.sol | 2 +- .../src/dispute/interfaces/IDelayedWETH.sol | 33 ++++++- .../src/universal/interfaces/IWETH98.sol | 2 + .../test/L1/DelayedVetoable.t.sol | 22 ++--- .../test/L1/SuperchainConfig.t.sol | 19 +++- .../test/L2/OptimismSuperchainERC20.t.sol | 30 +++--- .../test/L2/SequencerFeeVault.t.sol | 21 +++-- .../test/dispute/WETH98.t.sol | 12 ++- .../test/governance/MintManager.t.sol | 19 ++-- .../test/invariants/SystemConfig.t.sol | 18 +++- .../test/legacy/DeployerWhitelist.t.sol | 12 ++- .../test/legacy/L1BlockNumber.t.sol | 14 ++- .../test/legacy/ResolvedDelegateProxy.t.sol | 43 +++++++-- .../test/safe/DeputyGuardianModule.t.sol | 20 ++-- .../test/universal/BenchmarkTest.t.sol | 12 ++- .../test/universal/Proxy.t.sol | 12 ++- .../test/universal/ProxyAdmin.t.sol | 94 ++++++++++++------- .../test/vendor/InitializableOZv5.t.sol | 14 ++- semgrep/sol-rules.yaml | 1 + 19 files changed, 285 insertions(+), 115 deletions(-) diff --git a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol index c58c00b5a180..08bfd99f0131 100644 --- a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol +++ b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity ^0.8.0; // Scripts import { Vm } from "forge-std/Vm.sol"; diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol b/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol index 6db9cdeaada1..98b221285b56 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -interface IDelayedWETH is IWETH98 { +interface IDelayedWETH { struct WithdrawalRequest { uint256 amount; uint256 timestamp; @@ -30,7 +29,35 @@ interface IDelayedWETH is IWETH98 { function withdrawals(address, address) external view returns (uint256 amount, uint256 timestamp); function version() external view returns (string memory); - function withdraw(uint256 _wad) external override; + function withdraw(uint256 _wad) external; + + event Approval(address indexed src, address indexed guy, uint256 wad); + + event Transfer(address indexed src, address indexed dst, uint256 wad); + + event Deposit(address indexed dst, uint256 wad); + + event Withdrawal(address indexed src, uint256 wad); + + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); + + function balanceOf(address src) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + function deposit() external payable; + + function totalSupply() external view returns (uint256); + + function approve(address guy, uint256 wad) external returns (bool); + + function transfer(address dst, uint256 wad) external returns (bool); + + function transferFrom(address src, address dst, uint256 wad) external returns (bool); function __constructor__(uint256 _delay) external; } diff --git a/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol b/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol index 87f8844ae584..c7a77b7a36cd 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol @@ -36,4 +36,6 @@ interface IWETH98 { function transfer(address dst, uint256 wad) external returns (bool); function transferFrom(address src, address dst, uint256 wad) external returns (bool); + + function __constructor__() external; } diff --git a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol b/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol index 4d33a0784972..6eade75e8ffa 100644 --- a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol +++ b/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { DelayedVetoable } from "src/L1/DelayedVetoable.sol"; import { IDelayedVetoable } from "src/L1/interfaces/IDelayedVetoable.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract DelayedVetoable_Init is Test { error Unauthorized(address expected, address actual); @@ -27,14 +27,12 @@ contract DelayedVetoable_Init is Test { vm.deal(vetoer, 10000 ether); delayedVetoable = IDelayedVetoable( - address( - new DelayedVetoable({ - _initiator: initiator, - _vetoer: vetoer, - _target: address(target), - _operatingDelay: operatingDelay - }) - ) + DeployUtils.create1({ + _name: "DelayedVetoable", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IDelayedVetoable.__constructor__, (vetoer, initiator, address(target), operatingDelay)) + ) + }) ); // Most tests will use the operating delay, so we call as the initiator with null data @@ -155,7 +153,7 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init { contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { /// @dev Only the initiator can initiate a call. function test_handleCall_unauthorizedInitiation_reverts() external { - vm.expectRevert(abi.encodeWithSelector(DelayedVetoable.Unauthorized.selector, initiator, address(this))); + vm.expectRevert(abi.encodeWithSelector(IDelayedVetoable.Unauthorized.selector, initiator, address(this))); (bool revertsAsExpected,) = address(delayedVetoable).call(hex"00001234"); assertTrue(revertsAsExpected); } @@ -167,7 +165,7 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { (bool success,) = address(delayedVetoable).call(data); assertTrue(success); - vm.expectRevert(DelayedVetoable.ForwardingEarly.selector); + vm.expectRevert(IDelayedVetoable.ForwardingEarly.selector); (bool revertsAsExpected,) = address(delayedVetoable).call(data); assertTrue(revertsAsExpected); } @@ -191,7 +189,7 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { assertTrue(success); // Attempt to forward the same call again. - vm.expectRevert(abi.encodeWithSelector(DelayedVetoable.Unauthorized.selector, initiator, address(this))); + vm.expectRevert(abi.encodeWithSelector(IDelayedVetoable.Unauthorized.selector, initiator, address(this))); (bool revertsAsExpected,) = address(delayedVetoable).call(data); assertTrue(revertsAsExpected); } diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index ece5c68254c9..48206cd13ef9 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -4,12 +4,13 @@ pragma solidity 0.8.15; import { CommonTest } from "test/setup/CommonTest.sol"; // Target contract dependencies -import { Proxy } from "src/universal/Proxy.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; // Target contract -import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + contract SuperchainConfig_Init_Test is CommonTest { /// @dev Tests that initialization sets the correct values. These are defined in CommonTest.sol. function test_initialize_unpaused_succeeds() external view { @@ -19,8 +20,18 @@ contract SuperchainConfig_Init_Test is CommonTest { /// @dev Tests that it can be intialized as paused. function test_initialize_paused_succeeds() external { - Proxy newProxy = new Proxy(alice); - ISuperchainConfig newImpl = ISuperchainConfig(address(new SuperchainConfig())); + IProxy newProxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (alice))) + }) + ); + ISuperchainConfig newImpl = ISuperchainConfig( + DeployUtils.create1({ + _name: "SuperchainConfig", + _args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ())) + }) + ); vm.startPrank(alice); newProxy.upgradeToAndCall( diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol index 825e09bdd808..77ce7936b0fd 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol @@ -17,8 +17,8 @@ import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Target contract -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; /// @title OptimismSuperchainERC20Test /// @notice Contract for testing the OptimismSuperchainERC20 contract. @@ -31,12 +31,17 @@ contract OptimismSuperchainERC20Test is Test { address internal constant L2_BRIDGE = Predeploys.L2_STANDARD_BRIDGE; address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; - OptimismSuperchainERC20 public optimismSuperchainERC20Impl; - OptimismSuperchainERC20 public optimismSuperchainERC20; + IOptimismSuperchainERC20 public optimismSuperchainERC20Impl; + IOptimismSuperchainERC20 public optimismSuperchainERC20; /// @notice Sets up the test suite. function setUp() public { - optimismSuperchainERC20Impl = new OptimismSuperchainERC20(); + optimismSuperchainERC20Impl = IOptimismSuperchainERC20( + DeployUtils.create1({ + _name: "OptimismSuperchainERC20", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismSuperchainERC20.__constructor__, ())) + }) + ); // Deploy the OptimismSuperchainERC20Beacon contract _deployBeacon(); @@ -73,13 +78,13 @@ contract OptimismSuperchainERC20Test is Test { uint8 _decimals ) internal - returns (OptimismSuperchainERC20) + returns (IOptimismSuperchainERC20) { - return OptimismSuperchainERC20( + return IOptimismSuperchainERC20( address( new BeaconProxy( Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON, - abi.encodeCall(OptimismSuperchainERC20.initialize, (_remoteToken, _name, _symbol, _decimals)) + abi.encodeCall(IOptimismSuperchainERC20.initialize, (_remoteToken, _name, _symbol, _decimals)) ) ) ); @@ -219,25 +224,28 @@ contract OptimismSuperchainERC20Test is Test { /// @notice Tests the `decimals` function always returns the correct value. function testFuzz_decimals_succeeds(uint8 _decimals) public { - OptimismSuperchainERC20 _newSuperchainERC20 = _deploySuperchainERC20Proxy(REMOTE_TOKEN, NAME, SYMBOL, _decimals); + IOptimismSuperchainERC20 _newSuperchainERC20 = + _deploySuperchainERC20Proxy(REMOTE_TOKEN, NAME, SYMBOL, _decimals); assertEq(_newSuperchainERC20.decimals(), _decimals); } /// @notice Tests the `REMOTE_TOKEN` function always returns the correct value. function testFuzz_remoteToken_succeeds(address _remoteToken) public { - OptimismSuperchainERC20 _newSuperchainERC20 = _deploySuperchainERC20Proxy(_remoteToken, NAME, SYMBOL, DECIMALS); + IOptimismSuperchainERC20 _newSuperchainERC20 = _deploySuperchainERC20Proxy(_remoteToken, NAME, SYMBOL, DECIMALS); assertEq(_newSuperchainERC20.remoteToken(), _remoteToken); } /// @notice Tests the `name` function always returns the correct value. function testFuzz_name_succeeds(string memory _name) public { - OptimismSuperchainERC20 _newSuperchainERC20 = _deploySuperchainERC20Proxy(REMOTE_TOKEN, _name, SYMBOL, DECIMALS); + IOptimismSuperchainERC20 _newSuperchainERC20 = + _deploySuperchainERC20Proxy(REMOTE_TOKEN, _name, SYMBOL, DECIMALS); assertEq(_newSuperchainERC20.name(), _name); } /// @notice Tests the `symbol` function always returns the correct value. function testFuzz_symbol_succeeds(string memory _symbol) public { - OptimismSuperchainERC20 _newSuperchainERC20 = _deploySuperchainERC20Proxy(REMOTE_TOKEN, NAME, _symbol, DECIMALS); + IOptimismSuperchainERC20 _newSuperchainERC20 = + _deploySuperchainERC20Proxy(REMOTE_TOKEN, NAME, _symbol, DECIMALS); assertEq(_newSuperchainERC20.symbol(), _symbol); } diff --git a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol index 2d6ba4e94ec8..ca8c3806b38b 100644 --- a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol @@ -7,12 +7,13 @@ import { Reverter } from "test/mocks/Callers.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; // Contracts -import { SequencerFeeVault } from "src/L2/SequencerFeeVault.sol"; +import { ISequencerFeeVault } from "src/L2/interfaces/ISequencerFeeVault.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; import { Types } from "src/libraries/Types.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SequencerFeeVault_Test is CommonTest { address recipient; @@ -112,11 +113,19 @@ contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { vm.etch( EIP1967Helper.getImplementation(Predeploys.SEQUENCER_FEE_WALLET), address( - new SequencerFeeVault( - deploy.cfg().sequencerFeeVaultRecipient(), - deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), - Types.WithdrawalNetwork.L2 - ) + DeployUtils.create1({ + _name: "SequencerFeeVault", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + ISequencerFeeVault.__constructor__, + ( + deploy.cfg().sequencerFeeVaultRecipient(), + deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), + Types.WithdrawalNetwork.L2 + ) + ) + ) + }) ).code ); diff --git a/packages/contracts-bedrock/test/dispute/WETH98.t.sol b/packages/contracts-bedrock/test/dispute/WETH98.t.sol index a248e1901885..b26cd927f12c 100644 --- a/packages/contracts-bedrock/test/dispute/WETH98.t.sol +++ b/packages/contracts-bedrock/test/dispute/WETH98.t.sol @@ -5,7 +5,8 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; // Contracts -import { WETH98 } from "src/universal/WETH98.sol"; +import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract WETH98_Test is Test { event Approval(address indexed src, address indexed guy, uint256 wad); @@ -13,12 +14,17 @@ contract WETH98_Test is Test { event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); - WETH98 public weth; + IWETH98 public weth; address alice; address bob; function setUp() public { - weth = new WETH98(); + weth = IWETH98( + DeployUtils.create1({ + _name: "WETH98", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IWETH98.__constructor__, ())) + }) + ); alice = makeAddr("alice"); bob = makeAddr("bob"); deal(alice, 1 ether); diff --git a/packages/contracts-bedrock/test/governance/MintManager.t.sol b/packages/contracts-bedrock/test/governance/MintManager.t.sol index 4c008863ed69..c29e25602834 100644 --- a/packages/contracts-bedrock/test/governance/MintManager.t.sol +++ b/packages/contracts-bedrock/test/governance/MintManager.t.sol @@ -4,13 +4,10 @@ pragma solidity 0.8.15; // Testing import { CommonTest } from "test/setup/CommonTest.sol"; -// Contracts -import { GovernanceToken } from "src/governance/GovernanceToken.sol"; -import { MintManager } from "src/governance/MintManager.sol"; - // Interfaces import { IGovernanceToken } from "src/governance/interfaces/IGovernanceToken.sol"; import { IMintManager } from "src/governance/interfaces/IMintManager.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract MintManager_Initializer is CommonTest { address constant owner = address(0x1234); @@ -23,10 +20,20 @@ contract MintManager_Initializer is CommonTest { super.setUp(); vm.prank(owner); - gov = IGovernanceToken(address(new GovernanceToken())); + gov = IGovernanceToken( + DeployUtils.create1({ + _name: "GovernanceToken", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IGovernanceToken.__constructor__, ())) + }) + ); vm.prank(owner); - manager = IMintManager(address(new MintManager(owner, address(gov)))); + manager = IMintManager( + DeployUtils.create1({ + _name: "MintManager", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IMintManager.__constructor__, (owner, address(gov)))) + }) + ); vm.prank(owner); gov.transferOwnership(address(manager)); diff --git a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol index 1321499462b7..5b0b300abda0 100644 --- a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol @@ -2,17 +2,27 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { SystemConfig } from "src/L1/SystemConfig.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; -import { Proxy } from "src/universal/Proxy.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; import { Constants } from "src/libraries/Constants.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SystemConfig_GasLimitBoundaries_Invariant is Test { ISystemConfig public config; function setUp() external { - Proxy proxy = new Proxy(msg.sender); - ISystemConfig configImpl = ISystemConfig(address(new SystemConfig())); + IProxy proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + }) + ); + ISystemConfig configImpl = ISystemConfig( + DeployUtils.create1({ + _name: "SystemConfig", + _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) + }) + ); vm.prank(msg.sender); proxy.upgradeToAndCall( diff --git a/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol b/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol index ab624df2b322..3f140e9a0725 100644 --- a/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol +++ b/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol @@ -5,14 +5,20 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; // Target contract -import { DeployerWhitelist } from "src/legacy/DeployerWhitelist.sol"; +import { IDeployerWhitelist } from "src/legacy/interfaces/IDeployerWhitelist.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract DeployerWhitelist_Test is Test { - DeployerWhitelist list; + IDeployerWhitelist list; /// @dev Sets up the test suite. function setUp() public { - list = new DeployerWhitelist(); + list = IDeployerWhitelist( + DeployUtils.create1({ + _name: "DeployerWhitelist", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IDeployerWhitelist.__constructor__, ())) + }) + ); } /// @dev Tests that `owner` is initialized to the zero address. diff --git a/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol b/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol index 633a324e4043..49758739d2da 100644 --- a/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol +++ b/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol @@ -5,23 +5,29 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; // Contracts -import { L1BlockNumber } from "src/legacy/L1BlockNumber.sol"; +import { IL1BlockNumber } from "src/legacy/interfaces/IL1BlockNumber.sol"; import { L1Block } from "src/L2/L1Block.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract L1BlockNumberTest is Test { L1Block lb; - L1BlockNumber bn; + IL1BlockNumber bn; uint64 constant number = 99; /// @dev Sets up the test suite. function setUp() external { - vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, address(new L1Block()).code); + vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, vm.getDeployedCode("L1Block.sol:L1Block")); lb = L1Block(Predeploys.L1_BLOCK_ATTRIBUTES); - bn = new L1BlockNumber(); + bn = IL1BlockNumber( + DeployUtils.create1({ + _name: "L1BlockNumber", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1BlockNumber.__constructor__, ())) + }) + ); vm.prank(lb.DEPOSITOR_ACCOUNT()); lb.setL1BlockValues({ diff --git a/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol b/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol index 33a81bc2ca0e..84c743c97aa3 100644 --- a/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol +++ b/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol @@ -5,25 +5,40 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; // Target contract dependencies -import { AddressManager } from "src/legacy/AddressManager.sol"; +import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; // Target contract -import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol"; +import { IResolvedDelegateProxy } from "src/legacy/interfaces/IResolvedDelegateProxy.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract ResolvedDelegateProxy_Test is Test { - AddressManager internal addressManager; + IAddressManager internal addressManager; SimpleImplementation internal impl; SimpleImplementation internal proxy; /// @dev Sets up the test suite. function setUp() public { // Set up the address manager. - addressManager = new AddressManager(); + addressManager = IAddressManager( + DeployUtils.create1({ + _name: "AddressManager", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IAddressManager.__constructor__, ())) + }) + ); impl = new SimpleImplementation(); addressManager.setAddress("SimpleImplementation", address(impl)); // Set up the proxy. - proxy = SimpleImplementation(address(new ResolvedDelegateProxy(addressManager, "SimpleImplementation"))); + proxy = SimpleImplementation( + address( + DeployUtils.create1({ + _name: "ResolvedDelegateProxy", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IResolvedDelegateProxy.__constructor__, (addressManager, "SimpleImplementation")) + ) + }) + ) + ); } /// @dev Tests that the proxy properly bubbles up returndata when the delegatecall succeeds. @@ -42,8 +57,22 @@ contract ResolvedDelegateProxy_Test is Test { /// @dev Tests that the proxy fallback reverts as expected if the implementation within the /// address manager is not set. function test_fallback_addressManagerNotSet_reverts() public { - AddressManager am = new AddressManager(); - SimpleImplementation p = SimpleImplementation(address(new ResolvedDelegateProxy(am, "SimpleImplementation"))); + IAddressManager am = IAddressManager( + DeployUtils.create1({ + _name: "AddressManager", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IAddressManager.__constructor__, ())) + }) + ); + SimpleImplementation p = SimpleImplementation( + address( + DeployUtils.create1({ + _name: "ResolvedDelegateProxy", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IResolvedDelegateProxy.__constructor__, (am, "SimpleImplementation")) + ) + }) + ) + ); vm.expectRevert("ResolvedDelegateProxy: target address must be initialized"); p.foo(0); diff --git a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol index 47158c2ebace..2fec587211f7 100644 --- a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol +++ b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol @@ -8,7 +8,7 @@ import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; import "test/safe-tools/SafeTestTools.sol"; // Contracts -import { DeputyGuardianModule } from "src/safe/DeputyGuardianModule.sol"; +import { IDeputyGuardianModule } from "src/safe/interfaces/IDeputyGuardianModule.sol"; // Libraries import "src/dispute/lib/Types.sol"; @@ -17,6 +17,7 @@ import "src/dispute/lib/Types.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract DeputyGuardianModule_TestInit is CommonTest, SafeTestTools { using SafeTestLib for SafeInstance; @@ -26,7 +27,7 @@ contract DeputyGuardianModule_TestInit is CommonTest, SafeTestTools { event ExecutionFromModuleSuccess(address indexed); - DeputyGuardianModule deputyGuardianModule; + IDeputyGuardianModule deputyGuardianModule; SafeInstance safeInstance; address deputyGuardian; @@ -47,11 +48,16 @@ contract DeputyGuardianModule_TestInit is CommonTest, SafeTestTools { deputyGuardian = makeAddr("deputyGuardian"); - deputyGuardianModule = new DeputyGuardianModule({ - _safe: safeInstance.safe, - _superchainConfig: superchainConfig, - _deputyGuardian: deputyGuardian - }); + deputyGuardianModule = IDeputyGuardianModule( + DeployUtils.create1({ + _name: "DeputyGuardianModule", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + IDeputyGuardianModule.__constructor__, (safeInstance.safe, superchainConfig, deputyGuardian) + ) + ) + }) + ); safeInstance.enableModule(address(deputyGuardianModule)); } } diff --git a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol index f53310055e2b..454158566f6c 100644 --- a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol @@ -10,11 +10,12 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; // Libraries import { Types } from "src/libraries/Types.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; -import { L1BlockInterop } from "src/L2/L1BlockInterop.sol"; +import { IL1BlockInterop } from "src/L2/interfaces/IL1BlockInterop.sol"; import { Encoding } from "src/libraries/Encoding.sol"; // Interfaces import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Free function for setting the prevBaseFee param in the OptimismPortal. function setPrevBaseFee(Vm _vm, address _op, uint128 _prevBaseFee) { @@ -257,11 +258,16 @@ contract GasBenchMark_L1Block_SetValuesEcotone_Warm is GasBenchMark_L1Block { } contract GasBenchMark_L1BlockInterop is GasBenchMark_L1Block { - L1BlockInterop l1BlockInterop; + IL1BlockInterop l1BlockInterop; function setUp() public virtual override { super.setUp(); - l1BlockInterop = new L1BlockInterop(); + l1BlockInterop = IL1BlockInterop( + DeployUtils.create1({ + _name: "L1BlockInterop", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1BlockInterop.__constructor__, ())) + }) + ); setValuesCalldata = Encoding.encodeSetL1BlockValuesInterop( type(uint32).max, type(uint32).max, diff --git a/packages/contracts-bedrock/test/universal/Proxy.t.sol b/packages/contracts-bedrock/test/universal/Proxy.t.sol index 90d70d261775..9df4e1799aa0 100644 --- a/packages/contracts-bedrock/test/universal/Proxy.t.sol +++ b/packages/contracts-bedrock/test/universal/Proxy.t.sol @@ -2,8 +2,9 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { Proxy } from "src/universal/Proxy.sol"; import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SimpleStorage { mapping(uint256 => uint256) internal store; @@ -33,13 +34,18 @@ contract Proxy_Test is Test { bytes32 internal constant OWNER_KEY = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1); - Proxy proxy; + IProxy proxy; SimpleStorage simpleStorage; function setUp() external { // Deploy a proxy and simple storage contract as // the implementation - proxy = new Proxy(alice); + proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (alice))) + }) + ); simpleStorage = new SimpleStorage(); vm.prank(alice); diff --git a/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol b/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol index e212644c9d50..e6d6ced1f971 100644 --- a/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol +++ b/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol @@ -5,47 +5,73 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; import { SimpleStorage } from "test/universal/Proxy.t.sol"; -// Contracts -import { Proxy } from "src/universal/Proxy.sol"; -import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; -import { AddressManager } from "src/legacy/AddressManager.sol"; -import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol"; -import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol"; - // Interfaces import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; +import { IL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol"; +import { IResolvedDelegateProxy } from "src/legacy/interfaces/IResolvedDelegateProxy.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; + +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract ProxyAdmin_Test is Test { address alice = address(64); - Proxy proxy; - L1ChugSplashProxy chugsplash; - ResolvedDelegateProxy resolved; + IProxy proxy; + IL1ChugSplashProxy chugsplash; + IResolvedDelegateProxy resolved; - AddressManager addressManager; + IAddressManager addressManager; - ProxyAdmin admin; + IProxyAdmin admin; SimpleStorage implementation; function setUp() external { // Deploy the proxy admin - admin = new ProxyAdmin(alice); + admin = IProxyAdmin( + DeployUtils.create1({ + _name: "ProxyAdmin", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (alice))) + }) + ); + // Deploy the standard proxy - proxy = new Proxy(address(admin)); + proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(admin)))) + }) + ); // Deploy the legacy L1ChugSplashProxy with the admin as the owner - chugsplash = new L1ChugSplashProxy(address(admin)); + chugsplash = IL1ChugSplashProxy( + DeployUtils.create1({ + _name: "L1ChugSplashProxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ChugSplashProxy.__constructor__, (address(admin)))) + }) + ); // Deploy the legacy AddressManager - addressManager = new AddressManager(); + addressManager = IAddressManager( + DeployUtils.create1({ + _name: "AddressManager", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IAddressManager.__constructor__, ())) + }) + ); // The proxy admin must be the new owner of the address manager addressManager.transferOwnership(address(admin)); // Deploy a legacy ResolvedDelegateProxy with the name `a`. // Whatever `a` is set to in AddressManager will be the address // that is used for the implementation. - resolved = new ResolvedDelegateProxy(addressManager, "a"); - + resolved = IResolvedDelegateProxy( + DeployUtils.create1({ + _name: "ResolvedDelegateProxy", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IResolvedDelegateProxy.__constructor__, (addressManager, "a")) + ) + }) + ); // Impersonate alice for setting up the admin. vm.startPrank(alice); // Set the address of the address manager in the admin so that it @@ -57,9 +83,9 @@ contract ProxyAdmin_Test is Test { admin.setImplementationName(address(resolved), "a"); // Set the proxy types - admin.setProxyType(address(proxy), ProxyAdmin.ProxyType.ERC1967); - admin.setProxyType(address(chugsplash), ProxyAdmin.ProxyType.CHUGSPLASH); - admin.setProxyType(address(resolved), ProxyAdmin.ProxyType.RESOLVED); + admin.setProxyType(address(proxy), IProxyAdmin.ProxyType.ERC1967); + admin.setProxyType(address(chugsplash), IProxyAdmin.ProxyType.CHUGSPLASH); + admin.setProxyType(address(resolved), IProxyAdmin.ProxyType.RESOLVED); vm.stopPrank(); implementation = new SimpleStorage(); @@ -83,7 +109,7 @@ contract ProxyAdmin_Test is Test { function test_setProxyType_notOwner_reverts() external { vm.expectRevert("Ownable: caller is not the owner"); - admin.setProxyType(address(0), ProxyAdmin.ProxyType.CHUGSPLASH); + admin.setProxyType(address(0), IProxyAdmin.ProxyType.CHUGSPLASH); } function test_owner_succeeds() external view { @@ -91,9 +117,9 @@ contract ProxyAdmin_Test is Test { } function test_proxyType_succeeds() external view { - assertEq(uint256(admin.proxyType(address(proxy))), uint256(ProxyAdmin.ProxyType.ERC1967)); - assertEq(uint256(admin.proxyType(address(chugsplash))), uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); - assertEq(uint256(admin.proxyType(address(resolved))), uint256(ProxyAdmin.ProxyType.RESOLVED)); + assertEq(uint256(admin.proxyType(address(proxy))), uint256(IProxyAdmin.ProxyType.ERC1967)); + assertEq(uint256(admin.proxyType(address(chugsplash))), uint256(IProxyAdmin.ProxyType.CHUGSPLASH)); + assertEq(uint256(admin.proxyType(address(resolved))), uint256(IProxyAdmin.ProxyType.RESOLVED)); } function test_erc1967GetProxyImplementation_succeeds() external { @@ -153,7 +179,7 @@ contract ProxyAdmin_Test is Test { } function changeProxyAdmin(address payable _proxy) internal { - ProxyAdmin.ProxyType proxyType = admin.proxyType(address(_proxy)); + IProxyAdmin.ProxyType proxyType = admin.proxyType(address(_proxy)); vm.prank(alice); admin.changeProxyAdmin(_proxy, address(128)); @@ -162,13 +188,13 @@ contract ProxyAdmin_Test is Test { // no longer call the proxy interface except for // the ResolvedDelegate type on which anybody can // call the admin interface. - if (proxyType == ProxyAdmin.ProxyType.ERC1967) { + if (proxyType == IProxyAdmin.ProxyType.ERC1967) { vm.expectRevert("Proxy: implementation not initialized"); admin.getProxyAdmin(_proxy); - } else if (proxyType == ProxyAdmin.ProxyType.CHUGSPLASH) { + } else if (proxyType == IProxyAdmin.ProxyType.CHUGSPLASH) { vm.expectRevert("L1ChugSplashProxy: implementation is not set yet"); admin.getProxyAdmin(_proxy); - } else if (proxyType == ProxyAdmin.ProxyType.RESOLVED) { + } else if (proxyType == IProxyAdmin.ProxyType.RESOLVED) { // Just an empty block to show that all cases are covered } else { vm.expectRevert("ProxyAdmin: unknown proxy type"); @@ -177,11 +203,11 @@ contract ProxyAdmin_Test is Test { // Call the proxy contract directly to get the admin. // Different proxy types have different interfaces. vm.prank(address(128)); - if (proxyType == ProxyAdmin.ProxyType.ERC1967) { - assertEq(Proxy(payable(_proxy)).admin(), address(128)); - } else if (proxyType == ProxyAdmin.ProxyType.CHUGSPLASH) { - assertEq(L1ChugSplashProxy(payable(_proxy)).getOwner(), address(128)); - } else if (proxyType == ProxyAdmin.ProxyType.RESOLVED) { + if (proxyType == IProxyAdmin.ProxyType.ERC1967) { + assertEq(IProxy(payable(_proxy)).admin(), address(128)); + } else if (proxyType == IProxyAdmin.ProxyType.CHUGSPLASH) { + assertEq(IL1ChugSplashProxy(payable(_proxy)).getOwner(), address(128)); + } else if (proxyType == IProxyAdmin.ProxyType.RESOLVED) { assertEq(addressManager.owner(), address(128)); } else { assert(false); diff --git a/packages/contracts-bedrock/test/vendor/InitializableOZv5.t.sol b/packages/contracts-bedrock/test/vendor/InitializableOZv5.t.sol index 0820f987414a..4b663d697ba6 100644 --- a/packages/contracts-bedrock/test/vendor/InitializableOZv5.t.sol +++ b/packages/contracts-bedrock/test/vendor/InitializableOZv5.t.sol @@ -2,12 +2,13 @@ pragma solidity 0.8.25; import { Test } from "forge-std/Test.sol"; -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; - +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; /// @title InitializerOZv5_Test /// @dev Ensures that the `initialize()` function on contracts cannot be called more than /// once. Tests the contracts inheriting from `Initializable` from OpenZeppelin Contracts v5. + contract InitializerOZv5_Test is Test { /// @notice The storage slot of the `initialized` flag in the `Initializable` contract from OZ v5. /// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) @@ -31,8 +32,13 @@ contract InitializerOZv5_Test is Test { // OptimismSuperchainERC20 contracts.push( InitializeableContract({ - target: address(new OptimismSuperchainERC20()), - initCalldata: abi.encodeCall(OptimismSuperchainERC20.initialize, (address(0), "", "", 18)) + target: address( + DeployUtils.create1({ + _name: "OptimismSuperchainERC20", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismSuperchainERC20.__constructor__, ())) + }) + ), + initCalldata: abi.encodeCall(IOptimismSuperchainERC20.initialize, (address(0), "", "", 18)) }) ); } diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 5646182a04f2..71df6566d4c5 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -74,6 +74,7 @@ rules: exclude: - packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol - packages/contracts-bedrock/src/universal/interfaces/IWETH98.sol + - packages/contracts-bedrock/src/dispute/interfaces/IDelayedWETH.sol - op-chain-ops/script/testdata/scripts/ScriptExample.s.sol - packages/contracts-bedrock/test - packages/contracts-bedrock/scripts/libraries/Solarray.sol From 12d6cb3cea0c6990f6ad79f5ee5ae6c18bf00c8e Mon Sep 17 00:00:00 2001 From: DenseDenise Date: Sat, 26 Oct 2024 05:19:02 +0000 Subject: [PATCH 039/451] cannon: supplement the example cannon command (#12658) * optimize the example cannon command * Update cannon/README.md Co-authored-by: smartcontracts * Update cannon/README.md Co-authored-by: smartcontracts --------- Co-authored-by: smartcontracts --- cannon/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cannon/README.md b/cannon/README.md index be1615b45f8a..a5af5b2e7ece 100644 --- a/cannon/README.md +++ b/cannon/README.md @@ -39,6 +39,7 @@ make cannon # Note: # - The L2 RPC is an archive L2 node on OP MAINNET. # - The L1 RPC is a non-archive RPC, also change `--l1.rpckind` to reflect the correct L1 RPC type. +# - The network flag is only suitable for specific networks(https://github.com/ethereum-optimism/superchain-registry/blob/main/chainList.json). If you are running on the devnet, please use '--l2.genesis' to supply a path to the L2 devnet genesis file. ./bin/cannon run \ --pprof.cpu \ --info-at '%10000000' \ @@ -48,7 +49,7 @@ make cannon --input ./state.bin.gz \ -- \ ../op-program/bin/op-program \ - --network op-mainnet \ + --network \ --l1 \ --l2 \ --l1.head \ From 44f814cea84e03418fb993479795887c12e5391f Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Sat, 26 Oct 2024 12:42:12 +0700 Subject: [PATCH 040/451] feat(ci): check that Kontrol summary files are unchanged (#12592) Updates CI to check that the Kontrol summary dummy files are not modified by any PR unless the change is deliberate. Committed files are dummy files and aren't meant to change. Alternative here would be to generate the dummy files with a script that gets triggered before forge builds stuff. Main concern with using an autogen script is that the script would need to be added in front of build commands all over the place. Committing the files and checking that they aren't changed is slightly more janky but people rarely re-generate these files anyway so the projected impact is minimal. If this becomes a devx hassle we can consider autogen. --- .circleci/config.yml | 2 ++ packages/contracts-bedrock/justfile | 6 ++++ .../scripts/checks/check-file-unchanged.sh | 30 +++++++++++++++++++ .../check-kontrol-summaries-unchanged.sh | 10 +++++++ 4 files changed, 48 insertions(+) create mode 100755 packages/contracts-bedrock/scripts/checks/check-file-unchanged.sh create mode 100755 packages/contracts-bedrock/scripts/checks/check-kontrol-summaries-unchanged.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index a041bedb9ecb..bac526521686 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -676,6 +676,8 @@ jobs: - run: name: print forge version command: forge --version + - run-contracts-check: + command: check-kontrol-summaries-unchanged - run-contracts-check: command: semgrep-test-validity-check - run-contracts-check: diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 11bd9ed4946b..23f91c5779c3 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -185,6 +185,12 @@ validate-spacers-no-build: # Checks that spacer variables are correctly inserted. validate-spacers: build validate-spacers-no-build +# Checks that the Kontrol summary dummy files have not been modified. +# If you have changed the summary files deliberately, update the hashes in the script. +# Use `openssl dgst -sha256` to generate the hash for a file. +check-kontrol-summaries-unchanged: + ./scripts/checks/check-kontrol-summaries-unchanged.sh + # Runs semgrep on the contracts. semgrep: cd ../../ && semgrep scan --config=semgrep ./packages/contracts-bedrock diff --git a/packages/contracts-bedrock/scripts/checks/check-file-unchanged.sh b/packages/contracts-bedrock/scripts/checks/check-file-unchanged.sh new file mode 100755 index 000000000000..0949503efe32 --- /dev/null +++ b/packages/contracts-bedrock/scripts/checks/check-file-unchanged.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Check if both arguments are provided +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +file_path="$1" +expected_hash="$2" + +# Check if the file exists +if [ ! -f "$file_path" ]; then + echo "Error: File '$file_path' does not exist." + exit 1 +fi + +# Calculate the actual hash of the file +actual_hash=$(openssl dgst -sha256 -r "$file_path" | awk '{print $1}') + +# Compare the hashes +if [ "$actual_hash" = "$expected_hash" ]; then + exit 0 +else + echo "File '$file_path' has changed when it shouldn't have" + echo "Expected hash: $expected_hash" + echo "Actual hash: $actual_hash" + exit 1 +fi diff --git a/packages/contracts-bedrock/scripts/checks/check-kontrol-summaries-unchanged.sh b/packages/contracts-bedrock/scripts/checks/check-kontrol-summaries-unchanged.sh new file mode 100755 index 000000000000..32c764c188a8 --- /dev/null +++ b/packages/contracts-bedrock/scripts/checks/check-kontrol-summaries-unchanged.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Runs the file unchanged script for each of the Kontrol summary files. +# Update these hashes if you have changed the summary files deliberately. +# Use `openssl dgst -sha256` to generate the hash for a file. +./scripts/checks/check-file-unchanged.sh ./test/kontrol/proofs/utils/DeploymentSummary.sol 73e1ed1f3c8bd9e46bb348b774443458206b2399e4eac303e2a92ac987eeefc6 +./scripts/checks/check-file-unchanged.sh ./test/kontrol/proofs/utils/DeploymentSummaryCode.sol ffe7298024464a6a282ceb549db401cce4b36fe3342113250a6c674a3b4203ba +./scripts/checks/check-file-unchanged.sh ./test/kontrol/proofs/utils/DeploymentSummaryFaultProofs.sol 5ec8b19a00b47f15a2a8c8d632ab83933e19b0c2bbb04c905a2cda926e56038f +./scripts/checks/check-file-unchanged.sh ./test/kontrol/proofs/utils/DeploymentSummaryFaultProofsCode.sol 1215a561a2bffb5af62b532a2608c4048a22368e505f7ae1d237c16b4ef6a45e From f5591ca62fb0bbfeda5d708a81be723fca68b13d Mon Sep 17 00:00:00 2001 From: Delweng Date: Sat, 26 Oct 2024 15:10:28 +0800 Subject: [PATCH 041/451] op-service/client: rm unnecessary type arguments (#12651) Signed-off-by: jsvisa --- op-service/client/client.go | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/op-service/client/client.go b/op-service/client/client.go index 22f71866926b..9f4dac659424 100644 --- a/op-service/client/client.go +++ b/op-service/client/client.go @@ -79,43 +79,43 @@ func (ic *InstrumentedClient) RPC() RPC { } func (ic *InstrumentedClient) ChainID(ctx context.Context) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "eth_chainId", func() (*big.Int, error) { + return instrument2(ic.m, "eth_chainId", func() (*big.Int, error) { return ic.c.ChainID(ctx) }) } func (ic *InstrumentedClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return instrument2[*types.Block](ic.m, "eth_getBlockByHash", func() (*types.Block, error) { + return instrument2(ic.m, "eth_getBlockByHash", func() (*types.Block, error) { return ic.c.BlockByHash(ctx, hash) }) } func (ic *InstrumentedClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - return instrument2[*types.Block](ic.m, "eth_getBlockByNumber", func() (*types.Block, error) { + return instrument2(ic.m, "eth_getBlockByNumber", func() (*types.Block, error) { return ic.c.BlockByNumber(ctx, number) }) } func (ic *InstrumentedClient) BlockNumber(ctx context.Context) (uint64, error) { - return instrument2[uint64](ic.m, "eth_blockNumber", func() (uint64, error) { + return instrument2(ic.m, "eth_blockNumber", func() (uint64, error) { return ic.c.BlockNumber(ctx) }) } func (ic *InstrumentedClient) PeerCount(ctx context.Context) (uint64, error) { - return instrument2[uint64](ic.m, "net_peerCount", func() (uint64, error) { + return instrument2(ic.m, "net_peerCount", func() (uint64, error) { return ic.c.PeerCount(ctx) }) } func (ic *InstrumentedClient) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { - return instrument2[*types.Header](ic.m, "eth_getHeaderByHash", func() (*types.Header, error) { + return instrument2(ic.m, "eth_getHeaderByHash", func() (*types.Header, error) { return ic.c.HeaderByHash(ctx, hash) }) } func (ic *InstrumentedClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - return instrument2[*types.Header](ic.m, "eth_getHeaderByNumber", func() (*types.Header, error) { + return instrument2(ic.m, "eth_getHeaderByNumber", func() (*types.Header, error) { return ic.c.HeaderByNumber(ctx, number) }) } @@ -132,25 +132,25 @@ func (ic *InstrumentedClient) TransactionSender(ctx context.Context, tx *types.T } func (ic *InstrumentedClient) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - return instrument2[uint](ic.m, "eth_getTransactionCount", func() (uint, error) { + return instrument2(ic.m, "eth_getTransactionCount", func() (uint, error) { return ic.c.TransactionCount(ctx, blockHash) }) } func (ic *InstrumentedClient) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { - return instrument2[*types.Transaction](ic.m, "eth_getTransactionByBlockHashAndIndex", func() (*types.Transaction, error) { + return instrument2(ic.m, "eth_getTransactionByBlockHashAndIndex", func() (*types.Transaction, error) { return ic.c.TransactionInBlock(ctx, blockHash, index) }) } func (ic *InstrumentedClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - return instrument2[*types.Receipt](ic.m, "eth_getTransactionReceipt", func() (*types.Receipt, error) { + return instrument2(ic.m, "eth_getTransactionReceipt", func() (*types.Receipt, error) { return ic.c.TransactionReceipt(ctx, txHash) }) } func (ic *InstrumentedClient) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { - return instrument2[*ethereum.SyncProgress](ic.m, "eth_syncing", func() (*ethereum.SyncProgress, error) { + return instrument2(ic.m, "eth_syncing", func() (*ethereum.SyncProgress, error) { return ic.c.SyncProgress(ctx) }) } @@ -160,37 +160,37 @@ func (ic *InstrumentedClient) SubscribeNewHead(ctx context.Context, ch chan<- *t } func (ic *InstrumentedClient) NetworkID(ctx context.Context) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "net_version", func() (*big.Int, error) { + return instrument2(ic.m, "net_version", func() (*big.Int, error) { return ic.c.NetworkID(ctx) }) } func (ic *InstrumentedClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "eth_getBalance", func() (*big.Int, error) { + return instrument2(ic.m, "eth_getBalance", func() (*big.Int, error) { return ic.c.BalanceAt(ctx, account, blockNumber) }) } func (ic *InstrumentedClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_getStorageAt", func() ([]byte, error) { + return instrument2(ic.m, "eth_getStorageAt", func() ([]byte, error) { return ic.c.StorageAt(ctx, account, key, blockNumber) }) } func (ic *InstrumentedClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_getCode", func() ([]byte, error) { + return instrument2(ic.m, "eth_getCode", func() ([]byte, error) { return ic.c.CodeAt(ctx, account, blockNumber) }) } func (ic *InstrumentedClient) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - return instrument2[uint64](ic.m, "eth_getTransactionCount", func() (uint64, error) { + return instrument2(ic.m, "eth_getTransactionCount", func() (uint64, error) { return ic.c.NonceAt(ctx, account, blockNumber) }) } func (ic *InstrumentedClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - return instrument2[[]types.Log](ic.m, "eth_getLogs", func() ([]types.Log, error) { + return instrument2(ic.m, "eth_getLogs", func() ([]types.Log, error) { return ic.c.FilterLogs(ctx, q) }) } @@ -200,67 +200,67 @@ func (ic *InstrumentedClient) SubscribeFilterLogs(ctx context.Context, q ethereu } func (ic *InstrumentedClient) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "eth_getBalance", func() (*big.Int, error) { + return instrument2(ic.m, "eth_getBalance", func() (*big.Int, error) { return ic.c.PendingBalanceAt(ctx, account) }) } func (ic *InstrumentedClient) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_getStorageAt", func() ([]byte, error) { + return instrument2(ic.m, "eth_getStorageAt", func() ([]byte, error) { return ic.c.PendingStorageAt(ctx, account, key) }) } func (ic *InstrumentedClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_getCode", func() ([]byte, error) { + return instrument2(ic.m, "eth_getCode", func() ([]byte, error) { return ic.c.PendingCodeAt(ctx, account) }) } func (ic *InstrumentedClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - return instrument2[uint64](ic.m, "eth_getTransactionCount", func() (uint64, error) { + return instrument2(ic.m, "eth_getTransactionCount", func() (uint64, error) { return ic.c.PendingNonceAt(ctx, account) }) } func (ic *InstrumentedClient) PendingTransactionCount(ctx context.Context) (uint, error) { - return instrument2[uint](ic.m, "eth_getBlockTransactionCountByNumber", func() (uint, error) { + return instrument2(ic.m, "eth_getBlockTransactionCountByNumber", func() (uint, error) { return ic.c.PendingTransactionCount(ctx) }) } func (ic *InstrumentedClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_call", func() ([]byte, error) { + return instrument2(ic.m, "eth_call", func() ([]byte, error) { return ic.c.CallContract(ctx, msg, blockNumber) }) } func (ic *InstrumentedClient) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_call", func() ([]byte, error) { + return instrument2(ic.m, "eth_call", func() ([]byte, error) { return ic.c.CallContractAtHash(ctx, msg, blockHash) }) } func (ic *InstrumentedClient) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - return instrument2[[]byte](ic.m, "eth_call", func() ([]byte, error) { + return instrument2(ic.m, "eth_call", func() ([]byte, error) { return ic.c.PendingCallContract(ctx, msg) }) } func (ic *InstrumentedClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "eth_gasPrice", func() (*big.Int, error) { + return instrument2(ic.m, "eth_gasPrice", func() (*big.Int, error) { return ic.c.SuggestGasPrice(ctx) }) } func (ic *InstrumentedClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return instrument2[*big.Int](ic.m, "eth_maxPriorityFeePerGas", func() (*big.Int, error) { + return instrument2(ic.m, "eth_maxPriorityFeePerGas", func() (*big.Int, error) { return ic.c.SuggestGasPrice(ctx) }) } func (ic *InstrumentedClient) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { - return instrument2[uint64](ic.m, "eth_estimateGas", func() (uint64, error) { + return instrument2(ic.m, "eth_estimateGas", func() (uint64, error) { return ic.c.EstimateGas(ctx, msg) }) } From df8a3e39905e39878718a0887f1ac6b5bae94025 Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Sat, 26 Oct 2024 06:56:54 -0500 Subject: [PATCH 042/451] Fix DerivedFrom Client Signature (#12661) --- op-e2e/actions/interop/interop_test.go | 5 ++--- op-node/rollup/interop/interop.go | 8 ++++++-- op-node/rollup/interop/interop_test.go | 7 ++++++- op-service/sources/supervisor_client.go | 6 ++---- op-service/testutils/fake_interop_backend.go | 8 +++----- op-service/testutils/mock_interop_backend.go | 10 ++++------ 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/op-e2e/actions/interop/interop_test.go b/op-e2e/actions/interop/interop_test.go index b04a07da4826..1375275e178d 100644 --- a/op-e2e/actions/interop/interop_test.go +++ b/op-e2e/actions/interop/interop_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" @@ -137,8 +136,8 @@ func TestInteropVerifier(gt *testing.T) { out.Cross = request.Local return out, nil } - seqMockBackend.DerivedFromFn = func(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) { - require.Equal(t, uint64(1), blockNumber) + seqMockBackend.DerivedFromFn = func(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { + require.Equal(t, uint64(1), derived.Number) return l1Head, nil } seqMockBackend.FinalizedFn = func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { diff --git a/op-node/rollup/interop/interop.go b/op-node/rollup/interop/interop.go index 2421fcd31377..293402fee389 100644 --- a/op-node/rollup/interop/interop.go +++ b/op-node/rollup/interop/interop.go @@ -25,7 +25,7 @@ type InteropBackend interface { SafeView(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) - DerivedFrom(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) + DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error @@ -220,7 +220,11 @@ func (d *InteropDeriver) onCrossSafeUpdateEvent(x engine.CrossSafeUpdateEvent) e // and then reset derivation, so this op-node can help get the supervisor back in sync. return nil } - derivedFrom, err := d.backend.DerivedFrom(ctx, d.chainID, result.Cross.Hash, result.Cross.Number) + derived := eth.BlockID{ + Hash: result.Cross.Hash, + Number: result.Cross.Number, + } + derivedFrom, err := d.backend.DerivedFrom(ctx, d.chainID, derived) if err != nil { return fmt.Errorf("failed to get derived-from of %s: %w", result.Cross, err) } diff --git a/op-node/rollup/interop/interop_test.go b/op-node/rollup/interop/interop_test.go index 3dc0e7708d04..fc6bf59cacea 100644 --- a/op-node/rollup/interop/interop_test.go +++ b/op-node/rollup/interop/interop_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-node/rollup/finality" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" supervisortypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" @@ -114,7 +115,11 @@ func TestInteropDeriver(t *testing.T) { Cross: nextCrossSafe.ID(), } interopBackend.ExpectSafeView(chainID, localView, supervisorView, nil) - interopBackend.ExpectDerivedFrom(chainID, nextCrossSafe.Hash, nextCrossSafe.Number, derivedFrom, nil) + derived := eth.BlockID{ + Hash: nextCrossSafe.Hash, + Number: nextCrossSafe.Number, + } + interopBackend.ExpectDerivedFrom(chainID, derived, derivedFrom, nil) l2Source.ExpectL2BlockRefByHash(nextCrossSafe.Hash, nextCrossSafe, nil) emitter.ExpectOnce(engine.PromoteSafeEvent{ Ref: nextCrossSafe, diff --git a/op-service/sources/supervisor_client.go b/op-service/sources/supervisor_client.go index 48063e351e54..fc4c85b10a45 100644 --- a/op-service/sources/supervisor_client.go +++ b/op-service/sources/supervisor_client.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" @@ -99,9 +97,9 @@ func (cl *SupervisorClient) Finalized(ctx context.Context, chainID types.ChainID return result, err } -func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) { +func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { var result eth.L1BlockRef - err := cl.client.CallContext(ctx, &result, "supervisor_derivedFrom", chainID, blockHash, blockNumber) + err := cl.client.CallContext(ctx, &result, "supervisor_derivedFrom", chainID, derived) return result, err } diff --git a/op-service/testutils/fake_interop_backend.go b/op-service/testutils/fake_interop_backend.go index 7c6287031229..b73b4ac07225 100644 --- a/op-service/testutils/fake_interop_backend.go +++ b/op-service/testutils/fake_interop_backend.go @@ -3,8 +3,6 @@ package testutils import ( "context" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -13,7 +11,7 @@ type FakeInteropBackend struct { UnsafeViewFn func(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) SafeViewFn func(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) FinalizedFn func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) - DerivedFromFn func(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) + DerivedFromFn func(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) UpdateLocalUnsafeFn func(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error UpdateLocalSafeFn func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error UpdateFinalizedL1Fn func(ctx context.Context, chainID types.ChainID, finalized eth.L1BlockRef) error @@ -31,8 +29,8 @@ func (m *FakeInteropBackend) Finalized(ctx context.Context, chainID types.ChainI return m.FinalizedFn(ctx, chainID) } -func (m *FakeInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) { - return m.DerivedFromFn(ctx, chainID, blockHash, blockNumber) +func (m *FakeInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { + return m.DerivedFromFn(ctx, chainID, derived) } func (m *FakeInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { diff --git a/op-service/testutils/mock_interop_backend.go b/op-service/testutils/mock_interop_backend.go index 4bb300a95ff2..087b2f9c28cf 100644 --- a/op-service/testutils/mock_interop_backend.go +++ b/op-service/testutils/mock_interop_backend.go @@ -5,8 +5,6 @@ import ( "github.com/stretchr/testify/mock" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -60,13 +58,13 @@ func (m *MockInteropBackend) ExpectFinalized(chainID types.ChainID, result eth.B m.Mock.On("Finalized", chainID).Once().Return(result, &err) } -func (m *MockInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, blockHash common.Hash, blockNumber uint64) (eth.L1BlockRef, error) { - result := m.Mock.MethodCalled("DerivedFrom", chainID, blockHash, blockNumber) +func (m *MockInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { + result := m.Mock.MethodCalled("DerivedFrom", chainID, derived) return result.Get(0).(eth.L1BlockRef), *result.Get(1).(*error) } -func (m *MockInteropBackend) ExpectDerivedFrom(chainID types.ChainID, blockHash common.Hash, blockNumber uint64, result eth.L1BlockRef, err error) { - m.Mock.On("DerivedFrom", chainID, blockHash, blockNumber).Once().Return(result, &err) +func (m *MockInteropBackend) ExpectDerivedFrom(chainID types.ChainID, derived eth.BlockID, result eth.L1BlockRef, err error) { + m.Mock.On("DerivedFrom", chainID, derived).Once().Return(result, &err) } func (m *MockInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { From 71fd67043d4748e55ea6e43d9b6cc288e1207ffe Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sat, 26 Oct 2024 23:08:21 -0600 Subject: [PATCH 043/451] ci: fix goreleaser (#12667) The Docker release requires special args. --- .circleci/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bac526521686..27ac27532834 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1250,7 +1250,9 @@ jobs: steps: - setup_remote_docker - gcp-cli/install - - gcp-oidc-authenticate + - gcp-oidc-authenticate: + gcp_cred_config_file_path: /root/gcp_cred_config.json + oidc_token_file_path: /root/oidc_token.json - run: name: Install goreleaser pro command: | From 3e31ea4f5718963aae930bca5398b928c931c115 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Sun, 27 Oct 2024 11:07:29 +0100 Subject: [PATCH 044/451] Makefile: Fix executable command detection (#12080) `$(command -v geth)` is valid shell syntax, but it gets processed by make first, so that this never gets correctly executed by the shell. As a result, `make install-geth` and `make install-eth2-testnet-genesis` are always executed and not, as intended, just when the commands are not present. The fix is easy: escape the dollar by duplicating it, so that make won't do anything and the correct command reaches the shell. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 791eba38d453..3f6f759c59fc 100644 --- a/Makefile +++ b/Makefile @@ -173,10 +173,10 @@ nuke: clean devnet-clean ## Completely clean the project directory ## Prepares for running a local devnet pre-devnet: submodules $(DEVNET_CANNON_PRESTATE_FILES) - @if ! [ -x "$(command -v geth)" ]; then \ + @if ! [ -x "$$(command -v geth)" ]; then \ make install-geth; \ fi - @if ! [ -x "$(command -v eth2-testnet-genesis)" ]; then \ + @if ! [ -x "$$(command -v eth2-testnet-genesis)" ]; then \ make install-eth2-testnet-genesis; \ fi .PHONY: pre-devnet From 84ba885e668ae722a8349ce0a2702f81829deb88 Mon Sep 17 00:00:00 2001 From: I love OP Date: Sun, 27 Oct 2024 11:03:13 -0300 Subject: [PATCH 045/451] fix Makefile (#12668) --- op-chain-ops/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-chain-ops/Makefile b/op-chain-ops/Makefile index 6c4b57652855..99cd1aa80d2c 100644 --- a/op-chain-ops/Makefile +++ b/op-chain-ops/Makefile @@ -36,7 +36,7 @@ test: go test ./... op-deployer: - GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) CGO_ENABLED=0 go build -v $(LDFLAGS) -o ./bin/op-deployer ./cmd/op-deployer/main.go + GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) CGO_ENABLED=0 go build -v $(LDFLAGS) -o ./bin/op-deployer ../op-deployer/cmd/op-deployer/main.go fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeWithdrawal ./crossdomain @@ -49,4 +49,4 @@ sync-standard-version: curl -Lo ./deployer/opcm/standard-versions-mainnet.toml https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions-mainnet.toml curl -Lo ./deployer/opcm/standard-versions-sepolia.toml https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions-sepolia.toml -.PHONY: test fuzz op-deployer sync-standard-version \ No newline at end of file +.PHONY: test fuzz op-deployer sync-standard-version From 067446aa5528e46464f09c04c0e6b938b7aa81ac Mon Sep 17 00:00:00 2001 From: Julian Meyer Date: Sun, 27 Oct 2024 15:26:12 -0700 Subject: [PATCH 046/451] op-program: add experimental L2 source flag to enable execution witnesses (#12558) * feat: add experimental L2 source flag * Move L2 experimental client methods to eth client * Fix missing return * Improve config handling for experimental features * Add l2 experimental test --- op-e2e/actions/proofs/helpers/env.go | 2 +- op-program/host/cmd/main_test.go | 13 ++ op-program/host/config/config.go | 7 +- op-program/host/flags/flags.go | 6 + op-program/host/host.go | 24 ++-- op-program/host/l2_source.go | 151 +++++++++++++++++++++++ op-program/host/prefetcher/prefetcher.go | 5 + op-service/eth/execution_witness.go | 9 ++ op-service/sources/eth_client.go | 16 +++ 9 files changed, 215 insertions(+), 18 deletions(-) create mode 100644 op-program/host/l2_source.go create mode 100644 op-service/eth/execution_witness.go diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 4283d0d1e162..75b7f2554f4c 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -195,7 +195,7 @@ func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlock l2RPC := env.Engine.RPCClient() l2Client, err := host.NewL2Client(l2RPC, env.log, nil, &host.L2ClientConfig{L2ClientConfig: l2ClCfg, L2Head: cfg.L2Head}) require.NoError(t, err, "failed to create L2 client") - l2DebugCl := &host.L2Source{L2Client: l2Client, DebugClient: sources.NewDebugClient(l2RPC.CallContext)} + l2DebugCl := host.NewL2SourceWithClient(logger, l2Client, sources.NewDebugClient(l2RPC.CallContext)) return prefetcher.NewPrefetcher(logger, l1Cl, l1BlobFetcher, l2DebugCl, kv), nil }) diff --git a/op-program/host/cmd/main_test.go b/op-program/host/cmd/main_test.go index cee90a956646..35ca46d2472d 100644 --- a/op-program/host/cmd/main_test.go +++ b/op-program/host/cmd/main_test.go @@ -274,6 +274,19 @@ func TestL2Claim(t *testing.T) { }) } +func TestL2Experimental(t *testing.T) { + t.Run("DefaultEmpty", func(t *testing.T) { + cfg := configForArgs(t, addRequiredArgs()) + require.Equal(t, cfg.L2ExperimentalURL, "") + }) + + t.Run("Valid", func(t *testing.T) { + expected := "https://example.com:8545" + cfg := configForArgs(t, replaceRequiredArg("--l2.experimental", expected)) + require.EqualValues(t, expected, cfg.L2ExperimentalURL) + }) +} + func TestL2BlockNumber(t *testing.T) { t.Run("Required", func(t *testing.T) { verifyArgsInvalid(t, "flag l2.blocknumber is required", addRequiredArgsExcept("--l2.blocknumber")) diff --git a/op-program/host/config/config.go b/op-program/host/config/config.go index bb9f9d868e45..46442e07f43d 100644 --- a/op-program/host/config/config.go +++ b/op-program/host/config/config.go @@ -56,7 +56,11 @@ type Config struct { L2Head common.Hash // L2OutputRoot is the agreed L2 output root to start derivation from L2OutputRoot common.Hash - L2URL string + // L2URL is the URL of the L2 node to fetch L2 data from, this is the canonical URL for L2 data + // This URL is used as a fallback for L2ExperimentalURL if the experimental URL fails or cannot retrieve the desired data + L2URL string + // L2ExperimentalURL is the URL of the L2 node (non hash db archival node, for example, reth archival node) to fetch L2 data from + L2ExperimentalURL string // L2Claim is the claimed L2 output root to verify L2Claim common.Hash // L2ClaimBlockNumber is the block number the claimed L2 output root is from @@ -218,6 +222,7 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { DataDir: ctx.String(flags.DataDir.Name), DataFormat: dbFormat, L2URL: ctx.String(flags.L2NodeAddr.Name), + L2ExperimentalURL: ctx.String(flags.L2NodeExperimentalAddr.Name), L2ChainConfig: l2ChainConfig, L2Head: l2Head, L2OutputRoot: l2OutputRoot, diff --git a/op-program/host/flags/flags.go b/op-program/host/flags/flags.go index 15489cf22c94..b7c598451530 100644 --- a/op-program/host/flags/flags.go +++ b/op-program/host/flags/flags.go @@ -47,6 +47,11 @@ var ( Usage: "Address of L2 JSON-RPC endpoint to use (eth and debug namespace required)", EnvVars: prefixEnvVars("L2_RPC"), } + L2NodeExperimentalAddr = &cli.StringFlag{ + Name: "l2.experimental", + Usage: "Address of L2 JSON-RPC endpoint to use for experimental features (debug_executionWitness)", + EnvVars: prefixEnvVars("L2_RPC_EXPERIMENTAL_RPC"), + } L1Head = &cli.StringFlag{ Name: "l1.head", Usage: "Hash of the L1 head block. Derivation stops after this block is processed.", @@ -131,6 +136,7 @@ var programFlags = []cli.Flag{ DataDir, DataFormat, L2NodeAddr, + L2NodeExperimentalAddr, L2GenesisPath, L1NodeAddr, L1BeaconAddr, diff --git a/op-program/host/host.go b/op-program/host/host.go index d12920275558..cd30421f9383 100644 --- a/op-program/host/host.go +++ b/op-program/host/host.go @@ -24,11 +24,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -type L2Source struct { - *L2Client - *sources.DebugClient -} - type Prefetcher interface { Hint(hint string) error GetPreimage(ctx context.Context, key common.Hash) ([]byte, error) @@ -235,26 +230,23 @@ func makeDefaultPrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV return nil, fmt.Errorf("failed to setup L1 RPC: %w", err) } - logger.Info("Connecting to L2 node", "l2", cfg.L2URL) - l2RPC, err := client.NewRPC(ctx, logger, cfg.L2URL, client.WithDialAttempts(10)) - if err != nil { - return nil, fmt.Errorf("failed to setup L2 RPC: %w", err) - } - l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind) - l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true) l1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg) if err != nil { return nil, fmt.Errorf("failed to create L1 client: %w", err) } + + logger.Info("Connecting to L1 beacon", "l1", cfg.L1BeaconURL) l1Beacon := sources.NewBeaconHTTPClient(client.NewBasicHTTPClient(cfg.L1BeaconURL, logger)) l1BlobFetcher := sources.NewL1BeaconClient(l1Beacon, sources.L1BeaconClientConfig{FetchAllSidecars: false}) - l2Cl, err := NewL2Client(l2RPC, logger, nil, &L2ClientConfig{L2ClientConfig: l2ClCfg, L2Head: cfg.L2Head}) + + logger.Info("Initializing L2 clients") + l2Client, err := NewL2Source(ctx, logger, cfg) if err != nil { - return nil, fmt.Errorf("failed to create L2 client: %w", err) + return nil, fmt.Errorf("failed to create L2 source: %w", err) } - l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)} - return prefetcher.NewPrefetcher(logger, l1Cl, l1BlobFetcher, l2DebugCl, kv), nil + + return prefetcher.NewPrefetcher(logger, l1Cl, l1BlobFetcher, l2Client, kv), nil } func routeHints(logger log.Logger, hHostRW io.ReadWriter, hinter preimage.HintHandler) chan error { diff --git a/op-program/host/l2_source.go b/op-program/host/l2_source.go new file mode 100644 index 000000000000..38906f70b87a --- /dev/null +++ b/op-program/host/l2_source.go @@ -0,0 +1,151 @@ +package host + +import ( + "context" + "time" + + "github.com/ethereum-optimism/optimism/op-program/host/config" + "github.com/ethereum-optimism/optimism/op-program/host/prefetcher" + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// L2Source is a source of L2 data, it abstracts away the details of how to fetch L2 data between canonical and experimental sources. +// It also tracks metrics for each of the apis. Once experimental sources are stable, this will only route to the "experimental" source. +type L2Source struct { + logger log.Logger + + // canonical source, used as a fallback if experimental source is enabled but fails + // the underlying node should be a geth hash scheme archival node. + canonicalEthClient *L2Client + canonicalDebugClient *sources.DebugClient + + // experimental source, used as the primary source if enabled + experimentalClient *L2Client +} + +var _ prefetcher.L2Source = &L2Source{} + +// NewL2SourceWithClient creates a new L2 source with the given client as the canonical client. +// This doesn't configure the experimental source, but is useful for testing. +func NewL2SourceWithClient(logger log.Logger, canonicalL2Client *L2Client, canonicalDebugClient *sources.DebugClient) *L2Source { + source := &L2Source{ + logger: logger, + canonicalEthClient: canonicalL2Client, + canonicalDebugClient: canonicalDebugClient, + } + + return source +} + +func NewL2Source(ctx context.Context, logger log.Logger, config *config.Config) (*L2Source, error) { + logger.Info("Connecting to canonical L2 source", "url", config.L2URL) + + // eth_getProof calls are expensive and takes time, so we use a longer timeout + canonicalL2RPC, err := client.NewRPC(ctx, logger, config.L2URL, client.WithDialAttempts(10), client.WithCallTimeout(5*time.Minute)) + if err != nil { + return nil, err + } + canonicalDebugClient := sources.NewDebugClient(canonicalL2RPC.CallContext) + + canonicalL2ClientCfg := sources.L2ClientDefaultConfig(config.Rollup, true) + canonicalL2Client, err := NewL2Client(canonicalL2RPC, logger, nil, &L2ClientConfig{L2ClientConfig: canonicalL2ClientCfg, L2Head: config.L2Head}) + if err != nil { + return nil, err + } + + source := NewL2SourceWithClient(logger, canonicalL2Client, canonicalDebugClient) + + if len(config.L2ExperimentalURL) == 0 { + return source, nil + } + + logger.Info("Connecting to experimental L2 source", "url", config.L2ExperimentalURL) + // debug_executionWitness calls are expensive and takes time, so we use a longer timeout + experimentalRPC, err := client.NewRPC(ctx, logger, config.L2ExperimentalURL, client.WithDialAttempts(10), client.WithCallTimeout(5*time.Minute)) + if err != nil { + return nil, err + } + experimentalL2ClientCfg := sources.L2ClientDefaultConfig(config.Rollup, true) + experimentalL2Client, err := NewL2Client(experimentalRPC, logger, nil, &L2ClientConfig{L2ClientConfig: experimentalL2ClientCfg, L2Head: config.L2Head}) + if err != nil { + return nil, err + } + + source.experimentalClient = experimentalL2Client + + return source, nil +} + +func (l *L2Source) ExperimentalEnabled() bool { + return l.experimentalClient != nil +} + +// CodeByHash implements prefetcher.L2Source. +func (l *L2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { + if l.ExperimentalEnabled() { + // This means experimental source was not able to retrieve relevant information from eth_getProof or debug_executionWitness + // We should fall back to the canonical source, and log a warning, and record a metric + l.logger.Warn("Experimental source failed to retrieve code by hash, falling back to canonical source", "hash", hash) + } + return l.canonicalDebugClient.CodeByHash(ctx, hash) +} + +// NodeByHash implements prefetcher.L2Source. +func (l *L2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { + if l.ExperimentalEnabled() { + // This means experimental source was not able to retrieve relevant information from eth_getProof or debug_executionWitness + // We should fall back to the canonical source, and log a warning, and record a metric + l.logger.Warn("Experimental source failed to retrieve node by hash, falling back to canonical source", "hash", hash) + } + return l.canonicalDebugClient.NodeByHash(ctx, hash) +} + +// InfoAndTxsByHash implements prefetcher.L2Source. +func (l *L2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) { + if l.ExperimentalEnabled() { + return l.experimentalClient.InfoAndTxsByHash(ctx, blockHash) + } + return l.canonicalEthClient.InfoAndTxsByHash(ctx, blockHash) +} + +// OutputByRoot implements prefetcher.L2Source. +func (l *L2Source) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) { + if l.ExperimentalEnabled() { + return l.experimentalClient.OutputByRoot(ctx, root) + } + return l.canonicalEthClient.OutputByRoot(ctx, root) +} + +// ExecutionWitness implements prefetcher.L2Source. +func (l *L2Source) ExecutionWitness(ctx context.Context, blockNum uint64) (*eth.ExecutionWitness, error) { + if !l.ExperimentalEnabled() { + l.logger.Error("Experimental source is not enabled, cannot fetch execution witness", "blockNum", blockNum) + return nil, prefetcher.ErrExperimentalPrefetchDisabled + } + + // log errors, but return standard error so we know to retry with legacy source + witness, err := l.experimentalClient.ExecutionWitness(ctx, blockNum) + if err != nil { + l.logger.Error("Failed to fetch execution witness from experimental source", "blockNum", blockNum, "err", err) + return nil, prefetcher.ErrExperimentalPrefetchFailed + } + return witness, nil +} + +// GetProof implements prefetcher.L2Source. +func (l *L2Source) GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error) { + if l.ExperimentalEnabled() { + return l.experimentalClient.GetProof(ctx, address, storage, blockTag) + } + proof, err := l.canonicalEthClient.GetProof(ctx, address, storage, blockTag) + if err != nil { + l.logger.Error("Failed to fetch proof from canonical source", "address", address, "storage", storage, "blockTag", blockTag, "err", err) + return nil, prefetcher.ErrExperimentalPrefetchFailed + } + return proof, nil +} diff --git a/op-program/host/prefetcher/prefetcher.go b/op-program/host/prefetcher/prefetcher.go index cb5a52fa88ce..60aa67d63360 100644 --- a/op-program/host/prefetcher/prefetcher.go +++ b/op-program/host/prefetcher/prefetcher.go @@ -28,6 +28,11 @@ var ( precompileFailure = [1]byte{0} ) +var ( + ErrExperimentalPrefetchFailed = errors.New("experimental prefetch failed") + ErrExperimentalPrefetchDisabled = errors.New("experimental prefetch disabled") +) + var acceleratedPrecompiles = []common.Address{ common.BytesToAddress([]byte{0x1}), // ecrecover common.BytesToAddress([]byte{0x8}), // bn256Pairing diff --git a/op-service/eth/execution_witness.go b/op-service/eth/execution_witness.go new file mode 100644 index 000000000000..91f274b23d7e --- /dev/null +++ b/op-service/eth/execution_witness.go @@ -0,0 +1,9 @@ +package eth + +import "github.com/ethereum/go-ethereum/common/hexutil" + +type ExecutionWitness struct { + Keys map[string]hexutil.Bytes `json:"keys"` + Codes map[string]hexutil.Bytes `json:"codes"` + State map[string]hexutil.Bytes `json:"state"` +} diff --git a/op-service/sources/eth_client.go b/op-service/sources/eth_client.go index 0c83b52336a6..39587fd19750 100644 --- a/op-service/sources/eth_client.go +++ b/op-service/sources/eth_client.go @@ -315,6 +315,22 @@ func (s *EthClient) FetchReceipts(ctx context.Context, blockHash common.Hash) (e return info, receipts, nil } +// ExecutionWitness fetches execution witness data for a block number. +func (s *EthClient) ExecutionWitness(ctx context.Context, blockNum uint64) (*eth.ExecutionWitness, error) { + var witness *eth.ExecutionWitness + + err := s.client.CallContext(ctx, &witness, "debug_executionWitness", hexutil.EncodeUint64(blockNum), true) + if err != nil { + return nil, err + } + + if witness == nil { + return nil, ethereum.NotFound + } + + return witness, nil +} + // GetProof returns an account proof result, with any optional requested storage proofs. // The retrieval does sanity-check that storage proofs for the expected keys are present in the response, // but does not verify the result. Call accountResult.Verify(stateRoot) to verify the result. From 4696908a0ba14acb6e1c34fe02753b7a9c66a049 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sun, 27 Oct 2024 22:55:45 -0600 Subject: [PATCH 047/451] ci: Check out first (#12680) CircleCI barfs if there's anything in the project folder. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 27ac27532834..ac1a5c55702a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1253,6 +1253,7 @@ jobs: - gcp-oidc-authenticate: gcp_cred_config_file_path: /root/gcp_cred_config.json oidc_token_file_path: /root/oidc_token.json + - checkout - run: name: Install goreleaser pro command: | @@ -1265,7 +1266,6 @@ jobs: name: Configure Docker command: | gcloud auth configure-docker us-docker.pkg.dev - - checkout - run: name: Run goreleaser command: | From 06a7ce2301bbcc8663863468a01a2386222a0f0d Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sun, 27 Oct 2024 23:14:16 -0600 Subject: [PATCH 048/451] ci: Install goreleaser pro in a separate directory (#12681) Turns our GoReleaser also barfs if there's anything extraneous in the git directory. --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac1a5c55702a..f0789a504ef6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1257,11 +1257,11 @@ jobs: - run: name: Install goreleaser pro command: | - mkdir goreleaser + mkdir -p /tmp/goreleaser + cd /tmp/goreleaser curl -L -o goreleaser.tgz https://github.com/goreleaser/goreleaser-pro/releases/download/v2.3.2-pro/goreleaser-pro_Linux_x86_64.tar.gz tar -xzvf goreleaser.tgz mv goreleaser /usr/local/bin/goreleaser - rm -rf goreleaser - run: name: Configure Docker command: | From bf1b4d9751f56b72acc0910a9e18d9e06c07fdd4 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Mon, 28 Oct 2024 13:57:46 +0700 Subject: [PATCH 049/451] maint(ct): remove safe.json (#12669) Not sure how I managed to commit that... removes a safe.json file that was accidentally committed a few months ago. --- packages/contracts-bedrock/safe.json | 155 --------------------------- 1 file changed, 155 deletions(-) delete mode 100644 packages/contracts-bedrock/safe.json diff --git a/packages/contracts-bedrock/safe.json b/packages/contracts-bedrock/safe.json deleted file mode 100644 index 1168ce3d5150..000000000000 --- a/packages/contracts-bedrock/safe.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "version": "1.0", - "chainId": "11155111", - "createdAt": 1718216278, - "meta": { - "name": "Transactions Batch", - "description": "", - "txBuilderVersion": "1.16.5", - "createdFromSafeAddress": "0x422c32c10b16c442426Eb7E93b07f7B521294565", - "createdFromOwnerAddress": "", - "checksum": "0xc2d69412777b9d9ba16c95d12f3aca13820b37a8db5f9d436f63058c48994cde" - }, - "transactions": [ - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000176f7065726174696f6e735f73657175656e6365725f7632000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0xee8ca3b568c76a8fa53bf566fc0934edddf25deba2199a53772b6c3a05ce20f5b8d220f8", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000166f7065726174696f6e735f70726f706f7365725f763200000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0xee8ca3b5c790be67575b95a6b0c9f618fbdce9b72b6e7202233f0afe087bb641e3d1f0e8", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000146f7065726174696f6e735f67656c61746f5f7632000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0xee8ca3b5f666fe8af1ff0032744dcf71fbbeada5e47079f37425a8a0069350bbb508c3db", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000186f7065726174696f6e735f6368616c6c656e6765725f76310000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0xee8ca3b53fa0ce0493ae4a3e182ec7ed727076c63bf2d7761a43e328e118b2afdf86afc3", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0xe551cdaa0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000176f7065726174696f6e735f73657175656e6365725f763400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e10000000000000000000000000af8c77cfeb57620c4d9dcc81df75a1f0da7064af00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000008f23bb38f531600e5d8fddaaec41f13fab46e98c00000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008f23bb38f531600e5d8fddaaec41f13fab46e98c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0x3323b467000000000000000000000000a0ff2a54adc3fb33c44a141e67d194cf249258cb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006467148cd2000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000176f7065726174696f6e735f73657175656e6365725f763400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000176f7065726174696f6e735f73657175656e6365725f763400000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057e40", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000176f7065726174696f6e735f73657175656e6365725f7634000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0xe551cdaa0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000166f7065726174696f6e735f70726f706f7365725f76330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000af8c77cfeb57620c4d9dcc81df75a1f0da7064af00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000049277ee36a024120ee218127354c4a3591dc90a90000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000049277ee36a024120ee218127354c4a3591dc90a90000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000001158e460913d00000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0x3323b467000000000000000000000000a0ff2a54adc3fb33c44a141e67d194cf249258cb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006467148cd2000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000166f7065726174696f6e735f70726f706f7365725f76330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000166f7065726174696f6e735f70726f706f7365725f76330000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083d600", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000166f7065726174696f6e735f70726f706f7365725f763300000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0xe551cdaa0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000186f7065726174696f6e735f6368616c6c656e6765725f7632000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e10000000000000000000000000af8c77cfeb57620c4d9dcc81df75a1f0da7064af00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ffb026f67da0869eb3abb090cb7f015ce0925cdf00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000049277ee36a024120ee218127354c4a3591dc90a900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0x3323b467000000000000000000000000a0ff2a54adc3fb33c44a141e67d194cf249258cb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006467148cd2000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000186f7065726174696f6e735f6368616c6c656e6765725f7632000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000186f7065726174696f6e735f6368616c6c656e6765725f7632000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057e40", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000186f7065726174696f6e735f6368616c6c656e6765725f76320000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0xe551cdaa0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000146f7065726174696f6e735f67656c61746f5f7633000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000ff11afac4146a0babf7f9f042a22c8053a5467400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000600000000000000000000000007506c12a824d73d9b08564d5afc22c949434755e0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000cd438409d5cac9d2e076ac7bd0bf2377e99bb6e4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000007506c12a824d73d9b08564d5afc22c949434755e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002100000000000000000000000000cd438409d5cac9d2e076ac7bd0bf2377e99bb6e400000000000000000000000000000000000000000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0x2a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0", - "value": "0", - "data": "0x3323b467000000000000000000000000a0ff2a54adc3fb33c44a141e67d194cf249258cb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006467148cd2000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000146f7065726174696f6e735f67656c61746f5f763300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000146f7065726174696f6e735f67656c61746f5f763300000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083d600", - "contractMethod": null, - "contractInputsValues": null - }, - { - "to": "0xa0ff2a54adc3fb33c44a141e67d194cf249258cb", - "value": "0", - "data": "0x9bc94d010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000146f7065726174696f6e735f67656c61746f5f7633000000000000000000000000", - "contractMethod": null, - "contractInputsValues": null - } - ] -} \ No newline at end of file From be455ca4fc0862d15f29f9964d53d54de0b9e4fa Mon Sep 17 00:00:00 2001 From: protolambda Date: Mon, 28 Oct 2024 15:23:46 +0700 Subject: [PATCH 050/451] op-node: disable finality based on local-safe when interop is active (#12690) * op-node: disable finality based on local-safe when interop is active * op-e2e: fix interop action-test, relies on op-supervisor finality signal now --- op-e2e/actions/interop/interop_test.go | 4 +++ op-node/rollup/finality/finalizer.go | 11 ++++++++ op-node/rollup/finality/finalizer_test.go | 31 +++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/op-e2e/actions/interop/interop_test.go b/op-e2e/actions/interop/interop_test.go index 1375275e178d..b005baf5f0fa 100644 --- a/op-e2e/actions/interop/interop_test.go +++ b/op-e2e/actions/interop/interop_test.go @@ -191,6 +191,10 @@ func TestInteropVerifier(gt *testing.T) { require.Equal(t, l1Head, finalized) return nil } + // Allow the supervisor to promote the cross-safe L2 block to finalized. + seqMockBackend.FinalizedFn = func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { + return seq.SyncStatus().SafeL2.ID(), nil + } // signal that L1 finalized; the cross-safe block we have should get finalized too l1Miner.ActL1SafeNext(t) l1Miner.ActL1FinalizeNext(t) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index 046b94664e05..fb49bf7afd09 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -73,6 +73,8 @@ type Finalizer struct { ctx context.Context + cfg *rollup.Config + emitter event.Emitter // finalizedL1 is the currently perceived finalized L1 block. @@ -98,6 +100,7 @@ func NewFinalizer(ctx context.Context, log log.Logger, cfg *rollup.Config, l1Fet lookback := calcFinalityLookback(cfg) return &Finalizer{ ctx: ctx, + cfg: cfg, log: log, finalizedL1: eth.L1BlockRef{}, triedFinalizeAt: 0, @@ -253,6 +256,14 @@ func (fi *Finalizer) tryFinalize() { func (fi *Finalizer) onDerivedSafeBlock(l2Safe eth.L2BlockRef, derivedFrom eth.L1BlockRef) { fi.mu.Lock() defer fi.mu.Unlock() + + // Stop registering blocks after interop. + // Finality in interop is determined by the superchain backend, + // i.e. the op-supervisor RPC identifies which L2 block may be finalized. + if fi.cfg.IsInterop(l2Safe.Time) { + return + } + // remember the last L2 block that we fully derived from the given finality data if len(fi.finalityData) == 0 || fi.finalityData[len(fi.finalityData)-1].L1Block.Number < derivedFrom.Number { // prune finality data if necessary, before appending any data. diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index cdaf4006c0e0..e65b21abf623 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -472,4 +472,35 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) }) + + // The Finalizer does not promote any blocks to finalized status after interop. + // Blocks after interop are finalized with the interop deriver and interop backend. + t.Run("disable-after-interop", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelInfo) + l1F := &testutils.MockL1Source{} + defer l1F.AssertExpectations(t) + l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{ + InteropTime: &refC1.Time, + }, l1F) + fi.AttachEmitter(emitter) + + // now say C0 and C1 were included in D and became the new safe head + fi.OnEvent(engine.SafeDerivedEvent{Safe: refC0, DerivedFrom: refD}) + fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) + fi.OnEvent(derive.DeriverIdleEvent{Origin: refD}) + emitter.AssertExpectations(t) + + emitter.ExpectOnce(TryFinalizeEvent{}) + fi.OnEvent(FinalizeL1Event{FinalizedL1: refD}) + emitter.AssertExpectations(t) + + // C1 was Interop, C0 was not yet interop and can be finalized + emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refC0}) + fi.OnEvent(TryFinalizeEvent{}) + emitter.AssertExpectations(t) + }) } From db39e52aedcbdd06c5c6429b84c9a428b11bfb26 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Mon, 28 Oct 2024 19:01:29 +0700 Subject: [PATCH 051/451] maint(ct): remove block.json (#12693) Old file, not used anymore. --- .../contracts-bedrock/test/mocks/block.json | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 packages/contracts-bedrock/test/mocks/block.json diff --git a/packages/contracts-bedrock/test/mocks/block.json b/packages/contracts-bedrock/test/mocks/block.json deleted file mode 100644 index 7de13ed6135a..000000000000 --- a/packages/contracts-bedrock/test/mocks/block.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "hash": "0xfd3c5e25a80f54a53c58bd3ad8c076dc1c0cdbd44ec2164d2d2b8cc50481cb78", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "miner": "0x0000000000000000000000000000000000000000", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "number": "0x0", - "gasUsed": "0x0", - "gasLimit": "0x1c9c380", - "extraData": "0x", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x654caabb", - "difficulty": "0x0", - "totalDifficulty": "0x0", - "sealFields": [ - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000" - ], - "uncles": [], - "transactions": [], - "size": "0x202", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x0000000000000000", - "baseFeePerGas": "0x3b9aca00" -} From 2013c903e92ba741cff28431fb8128e461d6ab2b Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Mon, 28 Oct 2024 14:17:06 +0100 Subject: [PATCH 052/451] fix abi.encodecall semgrep check for tests (#12664) * fix abi.encodecall semgrep for tests * fix safe call test * rm unneccessary abi.encodeWithSignature, fix fuzz test * use comment for semgrep... * use comment for semgrep... * use pattern-not for expectRevert cases * fixes * fixes * try fix ci * fix... --- .../testdata/scripts/ScriptExample.s.sol | 19 +- .../src/libraries/Encoding.sol | 2 + .../src/libraries/LegacyCrossDomainUtils.sol | 1 + .../test/L1/L1CrossDomainMessenger.t.sol | 50 ++--- .../test/L1/L1ERC721Bridge.t.sol | 12 +- .../test/L1/L1StandardBridge.t.sol | 189 ++++++------------ .../test/L1/L2OutputOracle.t.sol | 2 +- .../test/L1/OptimismPortal.t.sol | 88 +++++--- .../test/L1/OptimismPortal2.t.sol | 70 ++++--- .../test/L1/SuperchainConfig.t.sol | 2 +- .../test/L1/SystemConfig.t.sol | 12 +- .../test/L1/SystemConfigInterop.t.sol | 6 +- .../test/L2/CrossDomainOwnable.t.sol | 2 +- .../test/L2/CrossDomainOwnable2.t.sol | 9 +- .../test/L2/CrossDomainOwnable3.t.sol | 9 +- .../test/L2/CrossL2Inbox.t.sol | 40 ++-- .../test/L2/GasPriceOracle.t.sol | 2 + .../test/L2/L2CrossDomainMessenger.t.sol | 24 +-- .../test/L2/L2ERC721Bridge.t.sol | 10 +- .../contracts-bedrock/test/L2/L2Genesis.t.sol | 4 +- .../test/L2/L2StandardBridge.t.sol | 164 +++++++-------- .../test/L2/L2StandardBridgeInterop.t.sol | 26 +-- .../test/L2/L2ToL2CrossDomainMessenger.t.sol | 22 +- .../test/L2/OptimismSuperchainERC20.t.sol | 6 +- .../test/L2/SuperchainTokenBridge.t.sol | 8 +- .../test/L2/SuperchainWETH.t.sol | 12 +- packages/contracts-bedrock/test/L2/WETH.t.sol | 4 +- .../test/dispute/AnchorStateRegistry.t.sol | 40 ++-- .../test/dispute/FaultDisputeGame.t.sol | 13 +- .../test/invariants/SafeCall.t.sol | 5 +- .../test/kontrol/proofs/L1ERC721Bridge.k.sol | 2 +- .../kontrol/proofs/L1StandardBridge.k.sol | 4 +- .../test/legacy/ResolvedDelegateProxy.t.sol | 4 +- .../test/libraries/SafeCall.t.sol | 16 +- .../test/periphery/Transactor.t.sol | 8 +- .../test/periphery/drippie/Drippie.t.sol | 12 +- .../test/periphery/op-nft/Optimist.t.sol | 24 +-- .../test/safe-tools/SafeTestTools.sol | 52 ++--- .../test/safe/DeputyGuardianModule.t.sol | 16 +- .../test/safe/LivenessGuard.t.sol | 3 +- .../test/safe/SafeSigners.t.sol | 1 + .../test/universal/BenchmarkTest.t.sol | 6 +- .../test/universal/CrossDomainMessenger.t.sol | 2 +- .../OptimismMintableERC20Factory.t.sol | 2 +- .../test/universal/Proxy.t.sol | 11 +- .../test/universal/ProxyAdmin.t.sol | 2 +- semgrep/sol-rules.yaml | 12 +- 47 files changed, 469 insertions(+), 561 deletions(-) diff --git a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol index f2f20e5ca14e..84b9c0381da4 100644 --- a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol +++ b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol @@ -19,9 +19,11 @@ interface Vm { library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - function _castLogPayloadViewToPure( - function(bytes memory) internal view fnIn - ) internal pure returns (function(bytes memory) internal pure fnOut) { + function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn) + internal + pure + returns (function(bytes memory) internal pure fnOut) + { assembly { fnOut := fnIn } @@ -42,30 +44,29 @@ library console { } function log(string memory p0) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); // nosemgrep: sol-style-use-abi-encodecall } function log(string memory p0, bool p1) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); // nosemgrep: sol-style-use-abi-encodecall } function log(string memory p0, uint256 p1) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); // nosemgrep: sol-style-use-abi-encodecall } function log(string memory p0, address p1) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); // nosemgrep: sol-style-use-abi-encodecall } function log(string memory p0, string memory p1, string memory p2) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); // nosemgrep: sol-style-use-abi-encodecall } } /// @title ScriptExample /// @notice ScriptExample is an example script. The Go forge script code tests that it can run this. contract ScriptExample { - address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); Vm internal constant vm = Vm(VM_ADDRESS); diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index edcdd4ed75e2..84d5f732f5f6 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -74,6 +74,7 @@ library Encoding { pure returns (bytes memory) { + // nosemgrep: sol-style-use-abi-encodecall return abi.encodeWithSignature("relayMessage(address,address,bytes,uint256)", _target, _sender, _data, _nonce); } @@ -97,6 +98,7 @@ library Encoding { pure returns (bytes memory) { + // nosemgrep: sol-style-use-abi-encodecall return abi.encodeWithSignature( "relayMessage(uint256,address,address,uint256,uint256,bytes)", _nonce, diff --git a/packages/contracts-bedrock/src/libraries/LegacyCrossDomainUtils.sol b/packages/contracts-bedrock/src/libraries/LegacyCrossDomainUtils.sol index 2a784ca1b6dc..a43bc2cecc4b 100644 --- a/packages/contracts-bedrock/src/libraries/LegacyCrossDomainUtils.sol +++ b/packages/contracts-bedrock/src/libraries/LegacyCrossDomainUtils.sol @@ -24,6 +24,7 @@ library LegacyCrossDomainUtils { pure returns (bytes memory) { + // nosemgrep: sol-style-use-abi-encodecall return abi.encodeWithSignature( "relayMessage(address,address,bytes,uint256)", _target, _sender, _message, _messageNonce ); diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 2c6b5b66456d..08a976aa0ec2 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -58,14 +58,16 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { // deposit transaction on the optimism portal should be called vm.expectCall( address(optimismPortal), - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - Predeploys.L2_CROSS_DOMAIN_MESSENGER, - 0, - l1CrossDomainMessenger.baseGas(hex"ff", 100), - false, - Encoding.encodeCrossDomainMessage( - l1CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff" + abi.encodeCall( + IOptimismPortal.depositTransaction, + ( + Predeploys.L2_CROSS_DOMAIN_MESSENGER, + 0, + l1CrossDomainMessenger.baseGas(hex"ff", 100), + false, + Encoding.encodeCrossDomainMessage( + l1CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff" + ) ) ) ); @@ -604,7 +606,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that the superchain config is called by the messengers paused function function test_pause_callsSuperchainConfig_succeeds() external { - vm.expectCall(address(superchainConfig), abi.encodeWithSelector(ISuperchainConfig.paused.selector)); + vm.expectCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, ())); l1CrossDomainMessenger.paused(); } @@ -624,20 +626,22 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { function test_sendMessage_customGasToken_noValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) ); // deposit transaction on the optimism portal should be called vm.expectCall( address(optimismPortal), - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - Predeploys.L2_CROSS_DOMAIN_MESSENGER, - 0, - l1CrossDomainMessenger.baseGas(hex"ff", 100), - false, - Encoding.encodeCrossDomainMessage( - l1CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff" + abi.encodeCall( + IOptimismPortal.depositTransaction, + ( + Predeploys.L2_CROSS_DOMAIN_MESSENGER, + 0, + l1CrossDomainMessenger.baseGas(hex"ff", 100), + false, + Encoding.encodeCrossDomainMessage( + l1CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff" + ) ) ) ); @@ -670,7 +674,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { function test_sendMessage_customGasToken_withValue_reverts() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.expectRevert("CrossDomainMessenger: cannot send value with custom gas token"); @@ -681,7 +685,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { function test_relayMessage_customGasToken_noValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); address target = address(0xabcd); @@ -721,7 +725,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { function test_relayMessage_customGasToken_withValue_reverts() external virtual { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.expectRevert("CrossDomainMessenger: value must be zero unless message is from a system address"); @@ -743,7 +747,7 @@ contract L1CrossDomainMessenger_ReinitReentryTest is Bridge_Initializer { // Common values used across functions uint256 constant messageValue = 50; - bytes constant selector = abi.encodeWithSelector(this.reinitAndReenter.selector); + bytes selector = abi.encodeCall(this.reinitAndReenter, ()); address sender; bytes32 hash; address target; @@ -760,7 +764,7 @@ contract L1CrossDomainMessenger_ReinitReentryTest is Bridge_Initializer { /// @dev This method will be called by the relayed message, and will attempt to reenter the relayMessage function /// exactly once. - function reinitAndReenter() public payable { + function reinitAndReenter() external payable { // only attempt the attack once if (!attacked) { attacked = true; diff --git a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol index 44feddc5031d..055ba45d0e1b 100644 --- a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol @@ -250,7 +250,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l1CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(Predeploys.L2_ERC721_BRIDGE) ); vm.prank(address(l1CrossDomainMessenger)); @@ -276,7 +276,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l1CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(alice) ); vm.prank(address(l1CrossDomainMessenger)); @@ -290,7 +290,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l1CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(Predeploys.L2_ERC721_BRIDGE) ); vm.prank(address(l1CrossDomainMessenger)); @@ -306,7 +306,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l1CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(Predeploys.L2_ERC721_BRIDGE) ); vm.prank(address(l1CrossDomainMessenger)); @@ -325,7 +325,7 @@ contract L1ERC721Bridge_Pause_Test is Bridge_Initializer { /// @dev Ensures that the `paused` function of the bridge contract actually calls the `paused` function of the /// `superchainConfig`. function test_pause_callsSuperchainConfig_succeeds() external { - vm.expectCall(address(superchainConfig), abi.encodeWithSelector(ISuperchainConfig.paused.selector)); + vm.expectCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, ())); l1ERC721Bridge.paused(); } @@ -354,7 +354,7 @@ contract L1ERC721Bridge_Pause_TestFail is Bridge_Initializer { vm.mockCall( address(l1ERC721Bridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1ERC721Bridge.otherBridge())) ); } diff --git a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol index f01572733bc4..760f602df21e 100644 --- a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol @@ -68,7 +68,7 @@ contract L1StandardBridge_Pause_Test is Bridge_Initializer { /// @dev Ensures that the `paused` function of the bridge contract actually calls the `paused` function of the /// `superchainConfig`. function test_pause_callsSuperchainConfig_succeeds() external { - vm.expectCall(address(superchainConfig), abi.encodeWithSelector(ISuperchainConfig.paused.selector)); + vm.expectCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, ())); l1StandardBridge.paused(); } @@ -99,7 +99,7 @@ contract L1StandardBridge_Pause_TestFail is Bridge_Initializer { vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.otherBridge())) ); } @@ -173,11 +173,13 @@ contract L1StandardBridge_Receive_Test is Bridge_Initializer { vm.expectCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, - address(l2StandardBridge), - abi.encodeWithSelector(StandardBridge.finalizeBridgeETH.selector, alice, alice, 100, hex""), - 200_000 + abi.encodeCall( + ICrossDomainMessenger.sendMessage, + ( + address(l2StandardBridge), + abi.encodeCall(StandardBridge.finalizeBridgeETH, (alice, alice, 100, hex"")), + 200_000 + ) ) ); @@ -193,7 +195,7 @@ contract L1StandardBridge_Receive_TestFail is Bridge_Initializer { function testFuzz_receive_customGasToken_reverts(uint256 _value) external { vm.prank(alice, alice); vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) ); vm.deal(alice, _value); (bool success, bytes memory data) = address(l1StandardBridge).call{ value: _value }(hex""); @@ -214,51 +216,35 @@ contract PreBridgeETH is Bridge_Initializer { uint256 version = 0; // Internal constant in the OptimismPortal: DEPOSIT_VERSION address l1MessengerAliased = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); - bytes memory message = - abi.encodeWithSelector(StandardBridge.finalizeBridgeETH.selector, alice, alice, value, hex"dead"); + bytes memory message = abi.encodeCall(StandardBridge.finalizeBridgeETH, (alice, alice, value, hex"dead")); if (isLegacy) { vm.expectCall( - address(l1StandardBridge), - value, - abi.encodeWithSelector(l1StandardBridge.depositETH.selector, 50000, hex"dead") + address(l1StandardBridge), value, abi.encodeCall(l1StandardBridge.depositETH, (50000, hex"dead")) ); } else { vm.expectCall( - address(l1StandardBridge), - value, - abi.encodeWithSelector(l1StandardBridge.bridgeETH.selector, 50000, hex"dead") + address(l1StandardBridge), value, abi.encodeCall(l1StandardBridge.bridgeETH, (50000, hex"dead")) ); } vm.expectCall( address(l1CrossDomainMessenger), value, - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l2StandardBridge), message, 50000 - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l2StandardBridge), message, 50000)) ); - bytes memory innerMessage = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l1StandardBridge), - address(l2StandardBridge), - value, - 50000, - message + bytes memory innerMessage = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l1StandardBridge), address(l2StandardBridge), value, 50000, message) ); uint64 baseGas = l1CrossDomainMessenger.baseGas(message, 50000); vm.expectCall( address(optimismPortal), value, - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - address(l2CrossDomainMessenger), - value, - baseGas, - false, - innerMessage + abi.encodeCall( + IOptimismPortal.depositTransaction, + (address(l2CrossDomainMessenger), value, baseGas, false, innerMessage) ) ); @@ -311,7 +297,7 @@ contract L1StandardBridge_DepositETH_TestFail is Bridge_Initializer { /// @dev Tests that depositing reverts with custom gas token. function test_depositETH_customGasToken_reverts() external { vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.prank(alice, alice); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); @@ -337,7 +323,7 @@ contract L1StandardBridge_BridgeETH_TestFail is PreBridgeETH { function test_bridgeETH_customGasToken_reverts() external { vm.prank(alice, alice); vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); @@ -356,50 +342,34 @@ contract PreBridgeETHTo is Bridge_Initializer { if (isLegacy) { vm.expectCall( - address(l1StandardBridge), - value, - abi.encodeWithSelector(l1StandardBridge.depositETHTo.selector, bob, 60000, hex"dead") + address(l1StandardBridge), value, abi.encodeCall(l1StandardBridge.depositETHTo, (bob, 60000, hex"dead")) ); } else { vm.expectCall( - address(l1StandardBridge), - value, - abi.encodeWithSelector(l1StandardBridge.bridgeETHTo.selector, bob, 60000, hex"dead") + address(l1StandardBridge), value, abi.encodeCall(l1StandardBridge.bridgeETHTo, (bob, 60000, hex"dead")) ); } - bytes memory message = - abi.encodeWithSelector(StandardBridge.finalizeBridgeETH.selector, alice, bob, value, hex"dead"); + bytes memory message = abi.encodeCall(StandardBridge.finalizeBridgeETH, (alice, bob, value, hex"dead")); // the L1 bridge should call // L1CrossDomainMessenger.sendMessage vm.expectCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l2StandardBridge), message, 60000 - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l2StandardBridge), message, 60000)) ); - bytes memory innerMessage = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l1StandardBridge), - address(l2StandardBridge), - value, - 60000, - message + bytes memory innerMessage = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l1StandardBridge), address(l2StandardBridge), value, 60000, message) ); uint64 baseGas = l1CrossDomainMessenger.baseGas(message, 60000); vm.expectCall( address(optimismPortal), - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - address(l2CrossDomainMessenger), - value, - baseGas, - false, - innerMessage + abi.encodeCall( + IOptimismPortal.depositTransaction, + (address(l2CrossDomainMessenger), value, baseGas, false, innerMessage) ) ); @@ -452,7 +422,7 @@ contract L1StandardBridge_DepositETHTo_TestFail is Bridge_Initializer { external { vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.deal(address(this), _value); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); @@ -484,7 +454,7 @@ contract L1StandardBridge_BridgeETHTo_TestFail is PreBridgeETHTo { external { vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.deal(address(this), _value); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); @@ -518,42 +488,28 @@ contract L1StandardBridge_DepositERC20_Test is Bridge_Initializer { L1Token.approve(address(l1StandardBridge), type(uint256).max); // The l1StandardBridge should transfer alice's tokens to itself - vm.expectCall( - address(L1Token), abi.encodeWithSelector(ERC20.transferFrom.selector, alice, address(l1StandardBridge), 100) - ); + vm.expectCall(address(L1Token), abi.encodeCall(ERC20.transferFrom, (alice, address(l1StandardBridge), 100))); - bytes memory message = abi.encodeWithSelector( - StandardBridge.finalizeBridgeERC20.selector, address(L2Token), address(L1Token), alice, alice, 100, hex"" + bytes memory message = abi.encodeCall( + StandardBridge.finalizeBridgeERC20, (address(L2Token), address(L1Token), alice, alice, 100, hex"") ); // the L1 bridge should call L1CrossDomainMessenger.sendMessage vm.expectCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l2StandardBridge), message, 10000 - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l2StandardBridge), message, 10000)) ); - bytes memory innerMessage = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l1StandardBridge), - address(l2StandardBridge), - 0, - 10000, - message + bytes memory innerMessage = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l1StandardBridge), address(l2StandardBridge), 0, 10000, message) ); uint64 baseGas = l1CrossDomainMessenger.baseGas(message, 10000); vm.expectCall( address(optimismPortal), - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - address(l2CrossDomainMessenger), - 0, - baseGas, - false, - innerMessage + abi.encodeCall( + IOptimismPortal.depositTransaction, (address(l2CrossDomainMessenger), 0, baseGas, false, innerMessage) ) ); @@ -609,18 +565,13 @@ contract L1StandardBridge_DepositERC20To_Test is Bridge_Initializer { uint256 version = 0; // Internal constant in the OptimismPortal: DEPOSIT_VERSION address l1MessengerAliased = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); - bytes memory message = abi.encodeWithSelector( - StandardBridge.finalizeBridgeERC20.selector, address(L2Token), address(L1Token), alice, bob, 1000, hex"" + bytes memory message = abi.encodeCall( + StandardBridge.finalizeBridgeERC20, (address(L2Token), address(L1Token), alice, bob, 1000, hex"") ); - bytes memory innerMessage = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l1StandardBridge), - address(l2StandardBridge), - 0, - 10000, - message + bytes memory innerMessage = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l1StandardBridge), address(l2StandardBridge), 0, 10000, message) ); uint64 baseGas = l1CrossDomainMessenger.baseGas(message, 10000); @@ -653,26 +604,16 @@ contract L1StandardBridge_DepositERC20To_Test is Bridge_Initializer { // the L1 bridge should call L1CrossDomainMessenger.sendMessage vm.expectCall( address(l1CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l2StandardBridge), message, 10000 - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l2StandardBridge), message, 10000)) ); // The L1 XDM should call OptimismPortal.depositTransaction vm.expectCall( address(optimismPortal), - abi.encodeWithSelector( - IOptimismPortal.depositTransaction.selector, - address(l2CrossDomainMessenger), - 0, - baseGas, - false, - innerMessage + abi.encodeCall( + IOptimismPortal.depositTransaction, (address(l2CrossDomainMessenger), 0, baseGas, false, innerMessage) ) ); - vm.expectCall( - address(L1Token), - abi.encodeWithSelector(ERC20.transferFrom.selector, alice, address(l1StandardBridge), 1000) - ); + vm.expectCall(address(L1Token), abi.encodeCall(ERC20.transferFrom, (alice, address(l1StandardBridge), 1000))); vm.prank(alice); l1StandardBridge.depositERC20To(address(L1Token), address(L2Token), bob, 1000, 10000, hex""); @@ -700,7 +641,7 @@ contract L1StandardBridge_FinalizeETHWithdrawal_Test is Bridge_Initializer { vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); // ensure that the messenger has ETH to call with @@ -722,11 +663,11 @@ contract L1StandardBridge_FinalizeETHWithdrawal_TestFail is Bridge_Initializer { external { vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(address(l1StandardBridge.messenger()), _value); @@ -761,11 +702,11 @@ contract L1StandardBridge_FinalizeERC20Withdrawal_Test is Bridge_Initializer { vm.expectEmit(address(l1StandardBridge)); emit ERC20BridgeFinalized(address(L1Token), address(L2Token), alice, alice, 100, hex""); - vm.expectCall(address(L1Token), abi.encodeWithSelector(ERC20.transfer.selector, alice, 100)); + vm.expectCall(address(L1Token), abi.encodeCall(ERC20.transfer, (alice, 100))); vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.prank(address(l1StandardBridge.messenger())); @@ -781,7 +722,7 @@ contract L1StandardBridge_FinalizeERC20Withdrawal_TestFail is Bridge_Initializer function test_finalizeERC20Withdrawal_notMessenger_reverts() external { vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.prank(address(28)); @@ -793,7 +734,7 @@ contract L1StandardBridge_FinalizeERC20Withdrawal_TestFail is Bridge_Initializer function test_finalizeERC20Withdrawal_notOtherBridge_reverts() external { vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(address(0))) ); vm.prank(address(l1StandardBridge.messenger())); @@ -808,7 +749,7 @@ contract L1StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { address messenger = address(l1StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(messenger, 100); @@ -826,13 +767,13 @@ contract L1StandardBridge_FinalizeBridgeETH_TestFail is Bridge_Initializer { function testFuzz_finalizeBridgeETH_customGasToken_reverts(uint256 _value, bytes calldata _extraData) external { vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(address(l1CrossDomainMessenger), _value); vm.prank(address(l1CrossDomainMessenger)); vm.mockCall( - address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) ); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); @@ -844,7 +785,7 @@ contract L1StandardBridge_FinalizeBridgeETH_TestFail is Bridge_Initializer { address messenger = address(l1StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(messenger, 100); @@ -858,7 +799,7 @@ contract L1StandardBridge_FinalizeBridgeETH_TestFail is Bridge_Initializer { address messenger = address(l1StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(messenger, 100); @@ -872,7 +813,7 @@ contract L1StandardBridge_FinalizeBridgeETH_TestFail is Bridge_Initializer { address messenger = address(l1StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.deal(messenger, 100); diff --git a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol index 12e4ae4bf401..9b18479d5255 100644 --- a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol +++ b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol @@ -422,7 +422,7 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_TestBase { vm.startPrank(EIP1967Helper.getAdmin(address(proxy))); // Reviewer note: the NextImpl() still uses reinitializer. If we want to remove that, we'll need to use a // two step upgrade with the Storage lib. - proxy.upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2)); + proxy.upgradeToAndCall(address(nextImpl), abi.encodeCall(NextImpl.initialize, (2))); assertEq(proxy.implementation(), address(nextImpl)); // Verify that the NextImpl contract initialized its values according as expected diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol index 5ada092ad6b6..028fc7587043 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol @@ -201,6 +201,7 @@ contract OptimismPortal_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds when msg.sender == tx.origin and non-custom gas is used. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_senderIsOrigin_succeeds( address _to, uint256 _mint, @@ -226,6 +227,7 @@ contract OptimismPortal_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds when msg.sender != tx.origin and non-custom gas is used. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_senderNotOrigin_succeeds( address _to, uint256 _mint, @@ -308,6 +310,7 @@ contract OptimismPortal_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds for an EOA. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_eoa_succeeds( address _to, uint64 _gasLimit, @@ -352,6 +355,7 @@ contract OptimismPortal_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds for a contract. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_contract_succeeds( address _to, uint64 _gasLimit, @@ -403,7 +407,7 @@ contract OptimismPortal_Test is CommonTest { uint256 ts = block.timestamp; vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode(Types.OutputProposal(bytes32(uint256(1)), uint128(ts), uint128(startingBlockNumber))) ); @@ -535,7 +539,7 @@ contract OptimismPortal_Test is CommonTest { } function test_depositERC20Transaction_balanceOverflow_reverts() external { - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(42), 18)); + vm.mockCall(address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(42), 18)); // The balance slot vm.store(address(optimismPortal), bytes32(uint256(61)), bytes32(type(uint256).max)); @@ -788,7 +792,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_targetToken_reverts() external { vm.mockCall( address(systemConfig), - abi.encodeWithSignature("gasPayingToken()"), + abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(_defaultTx.target), 18) ); @@ -832,7 +836,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // this case we just use bytes32(uint256(1)). vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode(bytes32(uint256(1)), _proposedBlockNumber) ); @@ -858,7 +862,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // Mock a startingTimestamp change on the L2 Oracle vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSignature("startingTimestamp()"), + abi.encodeCall(IL2OutputOracle.startingTimestamp, ()), abi.encode(block.timestamp + 1) ); @@ -887,7 +891,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // to finalize the withdrawal. vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode( Types.OutputProposal(bytes32(uint256(0)), uint128(block.timestamp), uint128(_proposedBlockNumber)) ) @@ -918,7 +922,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // finalization period. vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode(Types.OutputProposal(_outputRoot, uint128(block.timestamp + 1), uint128(_proposedBlockNumber))) ); @@ -954,7 +958,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { uint256 recentTimestamp = block.timestamp - 1; vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode(Types.OutputProposal(_outputRoot, uint128(recentTimestamp), uint128(_proposedBlockNumber))) ); @@ -1006,7 +1010,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode( Types.OutputProposal( Hashing.hashOutputRootProof(outputRootProof), @@ -1034,7 +1038,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // this contract's callPortalAndExpectRevert() function above. Types.WithdrawalTransaction memory _testTx = _defaultTx; _testTx.target = address(this); - _testTx.data = abi.encodeWithSelector(this.callPortalAndExpectRevert.selector); + _testTx.data = abi.encodeCall(this.callPortalAndExpectRevert, ()); // Get modified proof inputs. ( @@ -1055,7 +1059,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { uint256 finalizedTimestamp = block.timestamp - l2OutputOracle.FINALIZATION_PERIOD_SECONDS() - 1; vm.mockCall( address(optimismPortal.l2Oracle()), - abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector), + abi.encodePacked(IL2OutputOracle.getL2Output.selector), abi.encode(Types.OutputProposal(outputRoot, uint128(finalizedTimestamp), uint128(_proposedBlockNumber))) ); @@ -1074,6 +1078,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. + /// forge-config: ciheavy.fuzz.runs = 8192 function testDiff_finalizeWithdrawalTransaction_succeeds( address _sender, address _target, @@ -1129,7 +1134,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { // Setup the Oracle to return the outputRoot vm.mockCall( address(l2OutputOracle), - abi.encodeWithSelector(l2OutputOracle.getL2Output.selector), + abi.encodePacked(l2OutputOracle.getL2Output.selector), abi.encode(outputRoot, block.timestamp, 100) ); @@ -1176,7 +1181,7 @@ contract OptimismPortalUpgradeable_Test is CommonTest { // The value passed to the initialize must be larger than the last value // that initialize was called with. IProxy(payable(address(optimismPortal))).upgradeToAndCall( - address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2) + address(nextImpl), abi.encodeCall(NextImpl.initialize, (2)) ); assertEq(IProxy(payable(address(optimismPortal))).implementation(), address(nextImpl)); @@ -1196,6 +1201,7 @@ contract OptimismPortalResourceFuzz_Test is CommonTest { uint256 constant MAX_GAS_LIMIT = 30_000_000; /// @dev Test that various values of the resource metering config will not break deposits. + /// forge-config: ciheavy.fuzz.runs = 10000 function testFuzz_systemConfigDeposit_succeeds( uint32 _maxResourceLimit, uint8 _elasticityMultiplier, @@ -1249,9 +1255,7 @@ contract OptimismPortalResourceFuzz_Test is CommonTest { systemTxMaxGas: _systemTxMaxGas, maximumBaseFee: _maximumBaseFee }); - vm.mockCall( - address(systemConfig), abi.encodeWithSelector(systemConfig.resourceConfig.selector), abi.encode(rcfg) - ); + vm.mockCall(address(systemConfig), abi.encodeCall(systemConfig.resourceConfig, ()), abi.encode(rcfg)); // Set the resource params uint256 _prevBlockNum = block.number - _blockDiff; @@ -1309,7 +1313,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T token.approve(address(optimismPortal), _mint); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); @@ -1330,6 +1336,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `depositERC20Transaction` succeeds when msg.sender == tx.origin. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositERC20Transaction_senderIsOrigin_succeeds( address _to, uint256 _mint, @@ -1355,6 +1362,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `depositERC20Transaction` succeeds when msg.sender != tx.origin. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositERC20Transaction_senderNotOrigin_succeeds( address _to, uint256 _mint, @@ -1382,7 +1390,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositERC20Transaction` reverts when not enough of the token is approved. function test_depositERC20Transaction_notEnoughAmount_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(stdError.arithmeticError); // Deposit the token into the portal optimismPortal.depositERC20Transaction(address(0), 1, 0, 0, false, ""); @@ -1395,13 +1405,13 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T token.approve(address(optimismPortal), 100); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); - - // Mock the token balance vm.mockCall( - address(token), abi.encodeWithSelector(token.balanceOf.selector, address(optimismPortal)), abi.encode(0) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) ); + // Mock the token balance + vm.mockCall(address(token), abi.encodeCall(token.balanceOf, (address(optimismPortal))), abi.encode(0)); + // Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction uint64 gasLimit = optimismPortal.minimumGasLimit(0); @@ -1414,7 +1424,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositERC20Transaction` reverts when creating a contract with a non-zero target. function test_depositERC20Transaction_isCreationNotZeroTarget_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction uint64 gasLimit = optimismPortal.minimumGasLimit(0); @@ -1427,7 +1439,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositERC20Transaction` reverts when the gas limit is too low. function test_depositERC20Transaction_gasLimitTooLow_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(SmallGasLimit.selector); // Deposit the token into the portal @@ -1440,7 +1454,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T data[120_000] = 0x01; // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); uint64 gasLimit = optimismPortal.minimumGasLimit(120_001); vm.expectRevert(LargeCalldata.selector); @@ -1455,7 +1471,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T token.approve(address(optimismPortal), _amount); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Deposit the token into the portal optimismPortal.depositERC20Transaction(address(0), _amount, 0, optimismPortal.minimumGasLimit(0), false, ""); @@ -1471,7 +1489,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T token.approve(address(optimismPortal), _defaultTx.value); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Deposit the token into the portal optimismPortal.depositERC20Transaction( @@ -1490,9 +1510,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T vm.expectCall(_defaultTx.target, 0, _defaultTx.data); - vm.expectCall( - address(token), 0, abi.encodeWithSelector(token.transfer.selector, _defaultTx.target, _defaultTx.value) - ); + vm.expectCall(address(token), 0, abi.encodeCall(token.transfer, (_defaultTx.target, _defaultTx.value))); optimismPortal.finalizeWithdrawalTransaction(_defaultTx); @@ -1520,7 +1538,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T uint64(bound(_gasLimit, optimismPortal.minimumGasLimit(uint64(_data.length)), rcfg.maxResourceLimit)); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); bytes memory opaqueData = abi.encodePacked(uint256(0), _value, _gasLimit, _isCreation, _data); @@ -1541,6 +1561,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_customGasToken_noValue_senderIsOrigin_succeeds( address _to, uint256 _value, @@ -1564,6 +1585,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_customGasToken_noValue_senderNotOrigin_succeeds( address _to, uint256 _value, @@ -1589,7 +1611,9 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositTransaction` fails when a custom gas token is used and msg.value is non-zero. function test_depositTransaction_customGasToken_withValue_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(NoValue.selector); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index dd37b61e9bef..8cc56937bd33 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -211,6 +211,7 @@ contract OptimismPortal2_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds for an EOA. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_eoa_succeeds( address _to, uint64 _gasLimit, @@ -255,6 +256,7 @@ contract OptimismPortal2_Test is CommonTest { } /// @dev Tests that `depositTransaction` succeeds for a contract. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_contract_succeeds( address _to, uint64 _gasLimit, @@ -388,7 +390,7 @@ contract OptimismPortal2_Test is CommonTest { } function test_depositERC20Transaction_balanceOverflow_reverts() external { - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(42), 18)); + vm.mockCall(address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(42), 18)); // The balance slot vm.store(address(optimismPortal2), bytes32(uint256(61)), bytes32(type(uint256).max)); @@ -892,7 +894,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_targetToken_reverts() external { vm.mockCall( address(systemConfig), - abi.encodeWithSignature("gasPayingToken()"), + abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(_defaultTx.target), 18) ); @@ -974,7 +976,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1); // Mock a createdAt change in the dispute game. - vm.mockCall(address(game), abi.encodeWithSignature("createdAt()"), abi.encode(block.timestamp + 1)); + vm.mockCall(address(game), abi.encodeCall(game.createdAt, ()), abi.encode(block.timestamp + 1)); // Attempt to finalize the withdrawal vm.expectRevert("OptimismPortal: withdrawal timestamp less than dispute game creation timestamp"); @@ -1120,7 +1122,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { // this contract's callPortalAndExpectRevert() function above. Types.WithdrawalTransaction memory _testTx = _defaultTx; _testTx.target = address(this); - _testTx.data = abi.encodeWithSelector(this.callPortalAndExpectRevert.selector); + _testTx.data = abi.encodeCall(this.callPortalAndExpectRevert, ()); // Get modified proof inputs. ( @@ -1161,6 +1163,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. + /// forge-config: ciheavy.fuzz.runs = 8192 function testDiff_finalizeWithdrawalTransaction_succeeds( address _sender, address _target, @@ -1420,7 +1423,7 @@ contract OptimismPortal2_Upgradeable_Test is CommonTest { // The value passed to the initialize must be larger than the last value // that initialize was called with. IProxy(payable(address(optimismPortal2))).upgradeToAndCall( - address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2) + address(nextImpl), abi.encodeCall(NextImpl.initialize, (2)) ); assertEq(IProxy(payable(address(optimismPortal2))).implementation(), address(nextImpl)); @@ -1444,6 +1447,7 @@ contract OptimismPortal2_ResourceFuzz_Test is CommonTest { } /// @dev Test that various values of the resource metering config will not break deposits. + /// forge-config: ciheavy.fuzz.runs = 10000 function testFuzz_systemConfigDeposit_succeeds( uint32 _maxResourceLimit, uint8 _elasticityMultiplier, @@ -1498,9 +1502,7 @@ contract OptimismPortal2_ResourceFuzz_Test is CommonTest { systemTxMaxGas: _systemTxMaxGas, maximumBaseFee: _maximumBaseFee }); - vm.mockCall( - address(systemConfig), abi.encodeWithSelector(systemConfig.resourceConfig.selector), abi.encode(rcfg) - ); + vm.mockCall(address(systemConfig), abi.encodeCall(systemConfig.resourceConfig, ()), abi.encode(rcfg)); // Set the resource params uint256 _prevBlockNum = block.number - _blockDiff; @@ -1558,7 +1560,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal token.approve(address(optimismPortal2), _mint); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); @@ -1579,6 +1583,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `depositERC20Transaction` succeeds when msg.sender == tx.origin. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositERC20Transaction_senderIsOrigin_succeeds( address _to, uint256 _mint, @@ -1604,6 +1609,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `depositERC20Transaction` succeeds when msg.sender != tx.origin. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositERC20Transaction_senderNotOrigin_succeeds( address _to, uint256 _mint, @@ -1631,7 +1637,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositERC20Transaction` reverts when not enough of the token is approved. function test_depositERC20Transaction_notEnoughAmount_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(stdError.arithmeticError); // Deposit the token into the portal optimismPortal2.depositERC20Transaction(address(0), 1, 0, 0, false, ""); @@ -1644,13 +1652,13 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal token.approve(address(optimismPortal2), 100); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); - - // Mock the token balance vm.mockCall( - address(token), abi.encodeWithSelector(token.balanceOf.selector, address(optimismPortal)), abi.encode(0) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) ); + // Mock the token balance + vm.mockCall(address(token), abi.encodeCall(token.balanceOf, (address(optimismPortal))), abi.encode(0)); + // Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction uint64 gasLimit = optimismPortal2.minimumGasLimit(0); @@ -1663,7 +1671,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositERC20Transaction` reverts when creating a contract with a non-zero target. function test_depositERC20Transaction_isCreationNotZeroTarget_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction uint64 gasLimit = optimismPortal2.minimumGasLimit(0); @@ -1676,7 +1686,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositERC20Transaction` reverts when the gas limit is too low. function test_depositERC20Transaction_gasLimitTooLow_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(SmallGasLimit.selector); // Deposit the token into the portal @@ -1689,7 +1701,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal data[120_000] = 0x01; // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); uint64 gasLimit = optimismPortal2.minimumGasLimit(120_001); vm.expectRevert(LargeCalldata.selector); @@ -1704,7 +1718,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal token.approve(address(optimismPortal2), _amount); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Deposit the token into the portal optimismPortal2.depositERC20Transaction(address(0), _amount, 0, optimismPortal.minimumGasLimit(0), false, ""); @@ -1720,7 +1736,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal token.approve(address(optimismPortal2), _defaultTx.value); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); // Deposit the token into the portal optimismPortal2.depositERC20Transaction( @@ -1748,9 +1766,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal vm.expectCall(_defaultTx.target, 0, _defaultTx.data); - vm.expectCall( - address(token), 0, abi.encodeWithSelector(token.transfer.selector, _defaultTx.target, _defaultTx.value) - ); + vm.expectCall(address(token), 0, abi.encodeCall(token.transfer, (_defaultTx.target, _defaultTx.value))); optimismPortal2.finalizeWithdrawalTransaction(_defaultTx); @@ -1778,7 +1794,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal uint64(bound(_gasLimit, optimismPortal2.minimumGasLimit(uint64(_data.length)), rcfg.maxResourceLimit)); // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); bytes memory opaqueData = abi.encodePacked(uint256(0), _value, _gasLimit, _isCreation, _data); @@ -1799,6 +1817,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_customGasToken_noValue_senderIsOrigin_succeeds( address _to, uint256 _value, @@ -1822,6 +1841,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_depositTransaction_customGasToken_noValue_senderNotOrigin_succeeds( address _to, uint256 _value, @@ -1847,7 +1867,9 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositTransaction` fails when a custom gas token is used and msg.value is non-zero. function test_depositTransaction_customGasToken_withValue_reverts() external { // Mock the gas paying token to be the ERC20 token - vm.mockCall(address(systemConfig), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(token), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + ); vm.expectRevert(NoValue.selector); diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index 48206cd13ef9..2772ec0c2a3c 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -36,7 +36,7 @@ contract SuperchainConfig_Init_Test is CommonTest { vm.startPrank(alice); newProxy.upgradeToAndCall( address(newImpl), - abi.encodeWithSelector(ISuperchainConfig.initialize.selector, deploy.cfg().superchainConfigGuardian(), true) + abi.encodeCall(ISuperchainConfig.initialize, (deploy.cfg().superchainConfigGuardian(), true)) ); assertTrue(ISuperchainConfig(address(newProxy)).paused()); diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index 5bf2193c2319..dff0852fb40d 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -375,9 +375,9 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { vm.assume(bytes(_name).length <= 32); vm.assume(bytes(_symbol).length <= 32); - vm.mockCall(_token, abi.encodeWithSelector(token.decimals.selector), abi.encode(18)); - vm.mockCall(_token, abi.encodeWithSelector(token.name.selector), abi.encode(_name)); - vm.mockCall(_token, abi.encodeWithSelector(token.symbol.selector), abi.encode(_symbol)); + vm.mockCall(_token, abi.encodeCall(token.decimals, ()), abi.encode(18)); + vm.mockCall(_token, abi.encodeCall(token.name, ()), abi.encode(_name)); + vm.mockCall(_token, abi.encodeCall(token.symbol, ()), abi.encode(_symbol)); cleanStorageAndInit(_token); @@ -421,7 +421,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { /// @dev Tests that initialization fails if decimals are not 18. function test_initialize_customGasToken_wrongDecimals_fails() external { - vm.mockCall(address(token), abi.encodeWithSelector(token.decimals.selector), abi.encode(8)); + vm.mockCall(address(token), abi.encodeCall(token.decimals, ()), abi.encode(8)); vm.expectRevert("SystemConfig: bad decimals of gas paying token"); cleanStorageAndInit(address(token)); @@ -432,7 +432,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { string memory name = new string(32); name = string.concat(name, "a"); - vm.mockCall(address(token), abi.encodeWithSelector(token.name.selector), abi.encode(name)); + vm.mockCall(address(token), abi.encodeCall(token.name, ()), abi.encode(name)); vm.expectRevert("GasPayingToken: string cannot be greater than 32 bytes"); cleanStorageAndInit(address(token)); @@ -443,7 +443,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { string memory symbol = new string(33); symbol = string.concat(symbol, "a"); - vm.mockCall(address(token), abi.encodeWithSelector(token.symbol.selector), abi.encode(symbol)); + vm.mockCall(address(token), abi.encodeCall(token.symbol, ()), abi.encode(symbol)); vm.expectRevert("GasPayingToken: string cannot be greater than 32 bytes"); cleanStorageAndInit(address(token)); diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index 50e1ac91c977..426dba30c72a 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -41,9 +41,9 @@ contract SystemConfigInterop_Test is CommonTest { vm.assume(bytes(_name).length <= 32); vm.assume(bytes(_symbol).length <= 32); - vm.mockCall(_token, abi.encodeWithSelector(ERC20.decimals.selector), abi.encode(18)); - vm.mockCall(_token, abi.encodeWithSelector(ERC20.name.selector), abi.encode(_name)); - vm.mockCall(_token, abi.encodeWithSelector(ERC20.symbol.selector), abi.encode(_symbol)); + vm.mockCall(_token, abi.encodeCall(ERC20.decimals, ()), abi.encode(18)); + vm.mockCall(_token, abi.encodeCall(ERC20.name, ()), abi.encode(_name)); + vm.mockCall(_token, abi.encodeCall(ERC20.symbol, ()), abi.encode(_symbol)); vm.expectCall( address(optimismPortal), diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable.t.sol index 1e7c10556a53..c66bd1f94ab3 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable.t.sol @@ -68,7 +68,7 @@ contract CrossDomainOwnableThroughPortal_Test is CommonTest { _value: 0, _gasLimit: 30_000, _isCreation: false, - _data: abi.encodeWithSelector(XDomainSetter.set.selector, 1) + _data: abi.encodeCall(XDomainSetter.set, (1)) }); // Simulate the operation of the `op-node` by parsing data diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol index fa1244a0b0e1..a80f633fede2 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol @@ -58,7 +58,7 @@ contract CrossDomainOwnable2_Test is Bridge_Initializer { address target = address(setter); uint256 value = 0; uint256 minGasLimit = 0; - bytes memory message = abi.encodeWithSelector(XDomainSetter2.set.selector, 1); + bytes memory message = abi.encodeCall(XDomainSetter2.set, (1)); bytes32 hash = Hashing.hashCrossDomainMessage( Encoding.encodeVersionedNonce(nonce, 1), sender, target, value, minGasLimit, message @@ -85,12 +85,7 @@ contract CrossDomainOwnable2_Test is Bridge_Initializer { // the L1CrossDomainMessenger vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger))); l2CrossDomainMessenger.relayMessage( - Encoding.encodeVersionedNonce(1, 1), - owner, - address(setter), - 0, - 0, - abi.encodeWithSelector(XDomainSetter2.set.selector, 2) + Encoding.encodeVersionedNonce(1, 1), owner, address(setter), 0, 0, abi.encodeCall(XDomainSetter2.set, (2)) ); assertEq(setter.value(), 2); diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol index 432007ecd6d7..968910478198 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol @@ -101,7 +101,7 @@ contract CrossDomainOwnable3_Test is Bridge_Initializer { address target = address(setter); uint256 value = 0; uint256 minGasLimit = 0; - bytes memory message = abi.encodeWithSelector(XDomainSetter3.set.selector, 1); + bytes memory message = abi.encodeCall(XDomainSetter3.set, (1)); bytes32 hash = Hashing.hashCrossDomainMessage( Encoding.encodeVersionedNonce(nonce, 1), sender, target, value, minGasLimit, message @@ -216,12 +216,7 @@ contract CrossDomainOwnable3_Test is Bridge_Initializer { // the L1CrossDomainMessenger vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger))); l2CrossDomainMessenger.relayMessage( - Encoding.encodeVersionedNonce(1, 1), - bob, - address(setter), - 0, - 0, - abi.encodeWithSelector(XDomainSetter3.set.selector, 2) + Encoding.encodeVersionedNonce(1, 1), bob, address(setter), 0, 0, abi.encodeCall(XDomainSetter3.set, (2)) ); assertEq(setter.value(), 2); diff --git a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol index 643b684e07d9..9e79906efeec 100644 --- a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol @@ -160,7 +160,7 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -170,7 +170,7 @@ contract CrossL2InboxTest is Test { // Ensure that the chain ID is in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id.chainId)), returnData: abi.encode(true) }); @@ -222,27 +222,27 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); // Ensure that id1's chain ID is in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id1.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id1.chainId)), returnData: abi.encode(true) }); // Ensure that id2's chain ID is in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id2.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id2.chainId)), returnData: abi.encode(true) }); // Set the target and message for the reentrant call address target = address(this); - bytes memory message = abi.encodeWithSelector(this.mockReentrant.selector, _id2); + bytes memory message = abi.encodeCall(this.mockReentrant, (_id2)); // Ensure that the contract has enough balance to send with value vm.deal(address(this), _value); @@ -282,7 +282,7 @@ contract CrossL2InboxTest is Test { // Ensure it is a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(true) }); @@ -312,7 +312,7 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -346,7 +346,7 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -375,14 +375,14 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); // Ensure that the chain ID is NOT in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id.chainId)), returnData: abi.encode(false) }); @@ -419,14 +419,14 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); // Ensure that the chain ID is in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id.chainId)), returnData: abi.encode(true) }); @@ -451,14 +451,14 @@ contract CrossL2InboxTest is Test { // Ensure that the chain ID is in the dependency set vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id.chainId)), returnData: abi.encode(true) }); // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -474,7 +474,7 @@ contract CrossL2InboxTest is Test { // Ensure it is a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(true) }); @@ -497,7 +497,7 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -526,7 +526,7 @@ contract CrossL2InboxTest is Test { // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); @@ -553,14 +553,14 @@ contract CrossL2InboxTest is Test { // Ensure that the chain ID is NOT in the dependency set. vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId), + data: abi.encodeCall(IL1BlockInterop.isInDependencySet, (_id.chainId)), returnData: abi.encode(false) }); // Ensure is not a deposit transaction vm.mockCall({ callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeWithSelector(IL1BlockInterop.isDeposit.selector), + data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), returnData: abi.encode(false) }); diff --git a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol index ed4d598f4392..0a1d97c9351e 100644 --- a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol +++ b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol @@ -93,6 +93,7 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { /// @dev Tests that `setGasPrice` reverts since it was removed in bedrock. function test_setGasPrice_doesNotExist_reverts() external { + // nosemgrep: sol-style-use-abi-encodecall (bool success, bytes memory returndata) = address(gasPriceOracle).call(abi.encodeWithSignature("setGasPrice(uint256)", 1)); @@ -102,6 +103,7 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { /// @dev Tests that `setL1BaseFee` reverts since it was removed in bedrock. function test_setL1BaseFee_doesNotExist_reverts() external { + // nosemgrep: sol-style-use-abi-encodecall (bool success, bytes memory returndata) = address(gasPriceOracle).call(abi.encodeWithSignature("setL1BaseFee(uint256)", 1)); diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index 26fc9f2d039d..1dc5749d6d54 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -48,11 +48,9 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { Encoding.encodeCrossDomainMessage(l2CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff"); vm.expectCall( address(l2ToL1MessagePasser), - abi.encodeWithSelector( - IL2ToL1MessagePasser.initiateWithdrawal.selector, - address(l1CrossDomainMessenger), - l2CrossDomainMessenger.baseGas(hex"ff", 100), - xDomainCallData + abi.encodeCall( + IL2ToL1MessagePasser.initiateWithdrawal, + (address(l1CrossDomainMessenger), l2CrossDomainMessenger.baseGas(hex"ff", 100), xDomainCallData) ) ); @@ -230,17 +228,15 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that sendMessage succeeds with a custom gas token when the call value is zero. function test_sendMessage_customGasToken_noValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); bytes memory xDomainCallData = Encoding.encodeCrossDomainMessage(l2CrossDomainMessenger.messageNonce(), alice, recipient, 0, 100, hex"ff"); vm.expectCall( address(l2ToL1MessagePasser), - abi.encodeWithSelector( - IL2ToL1MessagePasser.initiateWithdrawal.selector, - address(l1CrossDomainMessenger), - l2CrossDomainMessenger.baseGas(hex"ff", 100), - xDomainCallData + abi.encodeCall( + IL2ToL1MessagePasser.initiateWithdrawal, + (address(l1CrossDomainMessenger), l2CrossDomainMessenger.baseGas(hex"ff", 100), xDomainCallData) ) ); @@ -272,7 +268,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that the sendMessage reverts when call value is non-zero with custom gas token. function test_sendMessage_customGasToken_withValue_reverts() external { // Mock the gasPayingToken function to return a custom gas token - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("CrossDomainMessenger: cannot send value with custom gas token"); l2CrossDomainMessenger.sendMessage{ value: 1 }(recipient, hex"ff", uint32(100)); @@ -281,7 +277,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that the relayMessage succeeds with a custom gas token when the call value is zero. function test_relayMessage_customGasToken_noValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); address target = address(0xabcd); address sender = address(l1CrossDomainMessenger); @@ -317,7 +313,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { /// The L1CrossDomainMessenger `sendMessage` function cannot send value with a custom gas token. function test_relayMessage_customGasToken_withValue_reverts() external virtual { // Mock the gasPayingToken function to return a custom gas token - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("CrossDomainMessenger: value must be zero unless message is from a system address"); l2CrossDomainMessenger.relayMessage{ value: 1 }( diff --git a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol index 6ee1312d94d7..58179cb93207 100644 --- a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol @@ -240,7 +240,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(l2CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l2CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(l1ERC721Bridge) ); vm.prank(address(l2CrossDomainMessenger)); @@ -264,7 +264,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { // to be compliant with the `IOptimismMintableERC721` interface. vm.mockCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(l2CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l2CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(l1ERC721Bridge) ); vm.prank(address(l2CrossDomainMessenger)); @@ -287,7 +287,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(l2CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l2CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(alice) ); vm.prank(address(l2CrossDomainMessenger)); @@ -301,7 +301,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(l2CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l2CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1ERC721Bridge)) ); vm.prank(address(l2CrossDomainMessenger)); @@ -316,7 +316,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { // Finalize a withdrawal. vm.mockCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(l2CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(l2CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1ERC721Bridge)) ); vm.prank(address(l2CrossDomainMessenger)); diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 0f5ad085a822..57b22e628eba 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -139,9 +139,7 @@ contract L2GenesisTest is Test { /// @notice Tests the genesis predeploys setup. function _test_genesis_predeploys(string memory _path, bool _useInterop) internal { // Set the useInterop value - vm.mockCall( - address(genesis.cfg()), abi.encodeWithSelector(genesis.cfg().useInterop.selector), abi.encode(_useInterop) - ); + vm.mockCall(address(genesis.cfg()), abi.encodeCall(genesis.cfg().useInterop, ()), abi.encode(_useInterop)); // Set the predeploy proxies into state genesis.setPredeployProxies(); diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol index 3a173e9743b4..d703ec95f64e 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol @@ -56,17 +56,11 @@ contract L2StandardBridge_Test is Bridge_Initializer { assertEq(address(l2ToL1MessagePasser).balance, 0); uint256 nonce = l2CrossDomainMessenger.messageNonce(); - bytes memory message = - abi.encodeWithSelector(IStandardBridge.finalizeBridgeETH.selector, alice, alice, 100, hex""); + bytes memory message = abi.encodeCall(IStandardBridge.finalizeBridgeETH, (alice, alice, 100, hex"")); uint64 baseGas = l2CrossDomainMessenger.baseGas(message, 200_000); - bytes memory withdrawalData = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l2StandardBridge), - address(l1StandardBridge), - 100, - 200_000, - message + bytes memory withdrawalData = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l2StandardBridge), address(l1StandardBridge), 100, 200_000, message) ); bytes32 withdrawalHash = Hashing.hashWithdrawal( Types.WithdrawalTransaction({ @@ -107,21 +101,16 @@ contract L2StandardBridge_Test is Bridge_Initializer { vm.expectCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, - address(l1StandardBridge), - message, - 200_000 // StandardBridge's RECEIVE_DEFAULT_GAS_LIMIT + abi.encodeCall( + ICrossDomainMessenger.sendMessage, + (address(l1StandardBridge), message, 200_000) // StandardBridge's RECEIVE_DEFAULT_GAS_LIMIT ) ); vm.expectCall( Predeploys.L2_TO_L1_MESSAGE_PASSER, - abi.encodeWithSelector( - IL2ToL1MessagePasser.initiateWithdrawal.selector, - address(l1CrossDomainMessenger), - baseGas, - withdrawalData + abi.encodeCall( + IL2ToL1MessagePasser.initiateWithdrawal, (address(l1CrossDomainMessenger), baseGas, withdrawalData) ) ); @@ -134,7 +123,7 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that the receive function reverts with custom gas token. function testFuzz_receive_customGasToken_reverts(uint256 _value) external { vm.prank(alice, alice); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.deal(alice, _value); (bool success, bytes memory data) = address(l2StandardBridge).call{ value: _value }(hex""); assertFalse(success); @@ -173,7 +162,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdraw` reverts with custom gas token. function test_withdraw_customGasToken_reverts() external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdraw(address(Predeploys.LEGACY_ERC20_ETH), 1, 1, hex""); @@ -181,7 +172,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdraw` reverts with custom gas token. function test_withdrawERC20_customGasToken_reverts() external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdraw(address(L1Token), 1, 1, hex""); @@ -190,7 +183,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdraw` reverts with custom gas token. function test_withdrawERC20WithValue_customGasToken_reverts() external { vm.deal(alice, 1 ether); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdraw{ value: 1 ether }(address(L1Token), 1, 1, hex""); @@ -199,7 +194,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdraw` with value reverts with custom gas token. function test_withdraw_customGasTokenWithValue_reverts() external { vm.deal(alice, 1 ether); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdraw{ value: 1 ether }(address(Predeploys.LEGACY_ERC20_ETH), 1, 1, hex""); @@ -207,7 +204,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdrawTo` reverts with custom gas token. function test_withdrawTo_customGasToken_reverts() external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdrawTo(address(Predeploys.LEGACY_ERC20_ETH), bob, 1, 1, hex""); @@ -215,7 +214,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdrawTo` reverts with custom gas token. function test_withdrawToERC20_customGasToken_reverts() external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdrawTo(address(L2Token), bob, 1, 1, hex""); @@ -224,7 +225,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdrawTo` reverts with custom gas token. function test_withdrawToERC20WithValue_customGasToken_reverts() external { vm.deal(alice, 1 ether); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdrawTo{ value: 1 ether }(address(L2Token), bob, 1, 1, hex""); @@ -233,7 +236,9 @@ contract L2StandardBridge_Test is Bridge_Initializer { /// @dev Tests that `withdrawTo` with value reverts with custom gas token. function test_withdrawTo_customGasTokenWithValue_reverts() external { vm.deal(alice, 1 ether); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(18))); + vm.mockCall( + address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + ); vm.expectRevert("L2StandardBridge: not supported with custom gas token"); vm.prank(alice, alice); l2StandardBridge.withdrawTo{ value: 1 ether }(address(Predeploys.LEGACY_ERC20_ETH), bob, 1, 1, hex""); @@ -277,18 +282,12 @@ contract PreBridgeERC20 is Bridge_Initializer { deal(_l2Token, alice, 100, true); assertEq(ERC20(_l2Token).balanceOf(alice), 100); uint256 nonce = l2CrossDomainMessenger.messageNonce(); - bytes memory message = abi.encodeWithSelector( - IStandardBridge.finalizeBridgeERC20.selector, address(L1Token), _l2Token, alice, alice, 100, hex"" - ); + bytes memory message = + abi.encodeCall(IStandardBridge.finalizeBridgeERC20, (address(L1Token), _l2Token, alice, alice, 100, hex"")); uint64 baseGas = l2CrossDomainMessenger.baseGas(message, 1000); - bytes memory withdrawalData = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l2StandardBridge), - address(l1StandardBridge), - 0, - 1000, - message + bytes memory withdrawalData = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l2StandardBridge), address(l1StandardBridge), 0, 1000, message) ); bytes32 withdrawalHash = Hashing.hashWithdrawal( Types.WithdrawalTransaction({ @@ -303,35 +302,29 @@ contract PreBridgeERC20 is Bridge_Initializer { if (_isLegacy) { vm.expectCall( - address(l2StandardBridge), - abi.encodeWithSelector(l2StandardBridge.withdraw.selector, _l2Token, 100, 1000, hex"") + address(l2StandardBridge), abi.encodeCall(l2StandardBridge.withdraw, (_l2Token, 100, 1000, hex"")) ); } else { vm.expectCall( address(l2StandardBridge), - abi.encodeWithSelector( - l2StandardBridge.bridgeERC20.selector, _l2Token, address(L1Token), 100, 1000, hex"" - ) + abi.encodeCall(l2StandardBridge.bridgeERC20, (_l2Token, address(L1Token), 100, 1000, hex"")) ); } vm.expectCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(ICrossDomainMessenger.sendMessage.selector, address(l1StandardBridge), message, 1000) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l1StandardBridge), message, 1000)) ); vm.expectCall( Predeploys.L2_TO_L1_MESSAGE_PASSER, - abi.encodeWithSelector( - IL2ToL1MessagePasser.initiateWithdrawal.selector, - address(l1CrossDomainMessenger), - baseGas, - withdrawalData + abi.encodeCall( + IL2ToL1MessagePasser.initiateWithdrawal, (address(l1CrossDomainMessenger), baseGas, withdrawalData) ) ); // The l2StandardBridge should burn the tokens - vm.expectCall(_l2Token, abi.encodeWithSelector(OptimismMintableERC20.burn.selector, alice, 100)); + vm.expectCall(_l2Token, abi.encodeCall(OptimismMintableERC20.burn, (alice, 100))); vm.expectEmit(true, true, true, true); emit WithdrawalInitiated(address(L1Token), _l2Token, alice, alice, 100, hex""); @@ -415,18 +408,12 @@ contract PreBridgeERC20To is Bridge_Initializer { deal(_l2Token, alice, 100, true); assertEq(ERC20(L2Token).balanceOf(alice), 100); uint256 nonce = l2CrossDomainMessenger.messageNonce(); - bytes memory message = abi.encodeWithSelector( - IStandardBridge.finalizeBridgeERC20.selector, address(L1Token), _l2Token, alice, bob, 100, hex"" - ); + bytes memory message = + abi.encodeCall(IStandardBridge.finalizeBridgeERC20, (address(L1Token), _l2Token, alice, bob, 100, hex"")); uint64 baseGas = l2CrossDomainMessenger.baseGas(message, 1000); - bytes memory withdrawalData = abi.encodeWithSelector( - ICrossDomainMessenger.relayMessage.selector, - nonce, - address(l2StandardBridge), - address(l1StandardBridge), - 0, - 1000, - message + bytes memory withdrawalData = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + (nonce, address(l2StandardBridge), address(l1StandardBridge), 0, 1000, message) ); bytes32 withdrawalHash = Hashing.hashWithdrawal( Types.WithdrawalTransaction({ @@ -467,34 +454,29 @@ contract PreBridgeERC20To is Bridge_Initializer { if (_isLegacy) { vm.expectCall( address(l2StandardBridge), - abi.encodeWithSelector(l2StandardBridge.withdrawTo.selector, _l2Token, bob, 100, 1000, hex"") + abi.encodeCall(l2StandardBridge.withdrawTo, (_l2Token, bob, 100, 1000, hex"")) ); } else { vm.expectCall( address(l2StandardBridge), - abi.encodeWithSelector( - l2StandardBridge.bridgeERC20To.selector, _l2Token, address(L1Token), bob, 100, 1000, hex"" - ) + abi.encodeCall(l2StandardBridge.bridgeERC20To, (_l2Token, address(L1Token), bob, 100, 1000, hex"")) ); } vm.expectCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector(ICrossDomainMessenger.sendMessage.selector, address(l1StandardBridge), message, 1000) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l1StandardBridge), message, 1000)) ); vm.expectCall( Predeploys.L2_TO_L1_MESSAGE_PASSER, - abi.encodeWithSelector( - IL2ToL1MessagePasser.initiateWithdrawal.selector, - address(l1CrossDomainMessenger), - baseGas, - withdrawalData + abi.encodeCall( + IL2ToL1MessagePasser.initiateWithdrawal, (address(l1CrossDomainMessenger), baseGas, withdrawalData) ) ); // The l2StandardBridge should burn the tokens - vm.expectCall(address(L2Token), abi.encodeWithSelector(OptimismMintableERC20.burn.selector, alice, 100)); + vm.expectCall(address(L2Token), abi.encodeCall(OptimismMintableERC20.burn, (alice, 100))); vm.prank(alice, alice); } @@ -524,7 +506,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { function test_finalizeBridgeETH_sendToSelf_reverts() external { vm.mockCall( address(l2StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) ); vm.deal(address(l2CrossDomainMessenger), 100); @@ -537,7 +519,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { function test_finalizeBridgeETH_sendToMessenger_reverts() external { vm.mockCall( address(l2StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) ); vm.deal(address(l2CrossDomainMessenger), 100); @@ -550,21 +532,16 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { function testFuzz_bridgeETH_succeeds(uint256 _value, uint32 _minGasLimit, bytes calldata _extraData) external { uint256 nonce = l2CrossDomainMessenger.messageNonce(); - bytes memory message = - abi.encodeWithSelector(IStandardBridge.finalizeBridgeETH.selector, alice, alice, _value, _extraData); + bytes memory message = abi.encodeCall(IStandardBridge.finalizeBridgeETH, (alice, alice, _value, _extraData)); vm.expectCall( - address(l2StandardBridge), - _value, - abi.encodeWithSelector(l2StandardBridge.bridgeETH.selector, _minGasLimit, _extraData) + address(l2StandardBridge), _value, abi.encodeCall(l2StandardBridge.bridgeETH, (_minGasLimit, _extraData)) ); vm.expectCall( address(l2CrossDomainMessenger), _value, - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l1StandardBridge), message, _minGasLimit - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l1StandardBridge), message, _minGasLimit)) ); vm.expectEmit(address(l2StandardBridge)); @@ -587,7 +564,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { /// @dev Tests that bridging reverts with custom gas token. function test_bridgeETH_customGasToken_reverts() external { vm.prank(alice, alice); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); l2StandardBridge.bridgeETH(50000, hex"dead"); @@ -600,19 +577,16 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { vm.expectCall( address(l2StandardBridge), _value, - abi.encodeWithSelector(l1StandardBridge.bridgeETHTo.selector, bob, _minGasLimit, _extraData) + abi.encodeCall(l1StandardBridge.bridgeETHTo, (bob, _minGasLimit, _extraData)) ); - bytes memory message = - abi.encodeWithSelector(IStandardBridge.finalizeBridgeETH.selector, alice, bob, _value, _extraData); + bytes memory message = abi.encodeCall(IStandardBridge.finalizeBridgeETH, (alice, bob, _value, _extraData)); // the L2 bridge should call // L2CrossDomainMessenger.sendMessage vm.expectCall( address(l2CrossDomainMessenger), - abi.encodeWithSelector( - ICrossDomainMessenger.sendMessage.selector, address(l1StandardBridge), message, _minGasLimit - ) + abi.encodeCall(ICrossDomainMessenger.sendMessage, (address(l1StandardBridge), message, _minGasLimit)) ); vm.expectEmit(address(l2StandardBridge)); @@ -641,7 +615,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ) external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); vm.deal(address(this), _value); l2StandardBridge.bridgeETHTo{ value: _value }(bob, _minGasLimit, _extraData); @@ -654,7 +628,7 @@ contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { address messenger = address(l2StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) ); vm.deal(messenger, 100); @@ -674,12 +648,12 @@ contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { address messenger = address(l2StandardBridge.messenger()); vm.mockCall( messenger, - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) ); vm.deal(address(l2CrossDomainMessenger), 1); vm.prank(address(l2CrossDomainMessenger)); - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingToken()"), abi.encode(address(1), uint8(2))); + vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("StandardBridge: cannot bridge ETH with custom gas token"); l2StandardBridge.finalizeBridgeETH(alice, alice, 1, hex""); diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol index e57bc8977659..fdfd1b0ec6d3 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol @@ -31,21 +31,17 @@ contract L2StandardBridgeInterop_Test is Bridge_Initializer { /// @notice Mock ERC20 decimals function _mockDecimals(address _token, uint8 _decimals) internal { - _mockAndExpect(_token, abi.encodeWithSelector(IERC20Metadata.decimals.selector), abi.encode(_decimals)); + _mockAndExpect(_token, abi.encodeCall(IERC20Metadata.decimals, ()), abi.encode(_decimals)); } /// @notice Mock ERC165 interface function _mockInterface(address _token, bytes4 _interfaceId, bool _supported) internal { - _mockAndExpect( - _token, abi.encodeWithSelector(IERC165.supportsInterface.selector, _interfaceId), abi.encode(_supported) - ); + _mockAndExpect(_token, abi.encodeCall(IERC165.supportsInterface, (_interfaceId)), abi.encode(_supported)); } /// @notice Mock factory deployment function _mockDeployments(address _factory, address _token, address _deployed) internal { - _mockAndExpect( - _factory, abi.encodeWithSelector(IOptimismERC20Factory.deployments.selector, _token), abi.encode(_deployed) - ); + _mockAndExpect(_factory, abi.encodeCall(IOptimismERC20Factory.deployments, (_token)), abi.encode(_deployed)); } /// @notice Assume a valid address for fuzzing @@ -198,12 +194,8 @@ contract L2StandardBridgeInterop_LegacyToSuper_Test is L2StandardBridgeInterop_T emit Converted(_from, _to, _caller, _amount); // Mock and expect the `burn` and `mint` functions - _mockAndExpect( - _from, abi.encodeWithSelector(IMintableAndBurnableERC20.burn.selector, _caller, _amount), abi.encode() - ); - _mockAndExpect( - _to, abi.encodeWithSelector(IMintableAndBurnableERC20.mint.selector, _caller, _amount), abi.encode() - ); + _mockAndExpect(_from, abi.encodeCall(IMintableAndBurnableERC20.burn, (_caller, _amount)), abi.encode()); + _mockAndExpect(_to, abi.encodeCall(IMintableAndBurnableERC20.mint, (_caller, _amount)), abi.encode()); // Act vm.prank(_caller); @@ -356,12 +348,8 @@ contract L2StandardBridgeInterop_SuperToLegacy_Test is L2StandardBridgeInterop_T emit Converted(_from, _to, _caller, _amount); // Mock and expect the `burn` and `mint` functions - _mockAndExpect( - _from, abi.encodeWithSelector(IMintableAndBurnableERC20.burn.selector, _caller, _amount), abi.encode() - ); - _mockAndExpect( - _to, abi.encodeWithSelector(IMintableAndBurnableERC20.mint.selector, _caller, _amount), abi.encode() - ); + _mockAndExpect(_from, abi.encodeCall(IMintableAndBurnableERC20.burn, (_caller, _amount)), abi.encode()); + _mockAndExpect(_to, abi.encodeCall(IMintableAndBurnableERC20.mint, (_caller, _amount)), abi.encode()); // Act vm.prank(_caller); diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index 2c413bcfbe89..1e5e04edc25b 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -227,7 +227,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -271,7 +271,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -312,7 +312,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Set the target and message for the reentrant call address target = address(this); - bytes memory message = abi.encodeWithSelector(this.mockTarget.selector, _source, _sender); + bytes memory message = abi.encodeCall(this.mockTarget, (_source, _sender)); bytes32 msgHash = keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message)); @@ -334,7 +334,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -399,7 +399,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Set the target and message for the reentrant call address target = address(this); - bytes memory message = abi.encodeWithSelector(this.mockTargetReentrant.selector, _source2, _nonce, _sender2); + bytes memory message = abi.encodeCall(this.mockTargetReentrant, (_source2, _nonce, _sender2)); // Ensure the target contract is called with the correct parameters vm.expectCall({ callee: target, msgValue: _value, data: message }); @@ -415,7 +415,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -497,7 +497,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -536,7 +536,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -578,7 +578,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -628,7 +628,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -678,7 +678,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage), + data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol index 77ce7936b0fd..8b83b2821796 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol @@ -63,11 +63,7 @@ contract OptimismSuperchainERC20Test is Test { EIP1967Helper.setImplementation(_addr, _impl); // Mock implementation address - vm.mockCall( - _impl, - abi.encodeWithSelector(IBeacon.implementation.selector), - abi.encode(address(optimismSuperchainERC20Impl)) - ); + vm.mockCall(_impl, abi.encodeCall(IBeacon.implementation, ()), abi.encode(address(optimismSuperchainERC20Impl))); } /// @notice Helper function to deploy a proxy of the OptimismSuperchainERC20 contract. diff --git a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol index e40904324d0c..2ff6cecfd12b 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol @@ -96,8 +96,8 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { abi.encodeCall(superchainTokenBridge.relayERC20, (address(superchainERC20), _sender, _to, _amount)); _mockAndExpect( Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector( - IL2ToL2CrossDomainMessenger.sendMessage.selector, _chainId, address(superchainTokenBridge), _message + abi.encodeCall( + IL2ToL2CrossDomainMessenger.sendMessage, (_chainId, address(superchainTokenBridge), _message) ), abi.encode(_msgHash) ); @@ -150,7 +150,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { // Mock the call over the `crossDomainMessageContext` function setting a wrong sender vm.mockCall( Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageContext.selector), + abi.encodeCall(IL2ToL2CrossDomainMessenger.crossDomainMessageContext, ()), abi.encode(_crossDomainMessageSender, _source) ); @@ -169,7 +169,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { // Mock the call over the `crossDomainMessageContext` function setting the same address as value _mockAndExpect( Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageContext.selector), + abi.encodeCall(IL2ToL2CrossDomainMessenger.crossDomainMessageContext, ()), abi.encode(address(superchainTokenBridge), _source) ); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index b428dc4cd191..9a65544dc7ee 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -322,7 +322,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the internal mint function reverts to protect against accidentally changing the visibility. function testFuzz_calling_internal_mint_function_reverts(address _caller, address _to, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("_mint(address,uint256)", _to, _amount); + bytes memory _calldata = abi.encodeWithSignature("_mint(address,uint256)", _to, _amount); // nosemgrep: + // sol-style-use-abi-encodecall vm.expectRevert(bytes("")); // Act @@ -336,7 +337,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the mint function reverts to protect against accidentally changing the visibility. function testFuzz_calling_mint_function_reverts(address _caller, address _to, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("mint(address,uint256)", _to, _amount); + bytes memory _calldata = abi.encodeWithSignature("mint(address,uint256)", _to, _amount); // nosemgrep: + // sol-style-use-abi-encodecall vm.expectRevert(bytes("")); // Act @@ -350,7 +352,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the internal burn function reverts to protect against accidentally changing the visibility. function testFuzz_calling_internal_burn_function_reverts(address _caller, address _from, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("_burn(address,uint256)", _from, _amount); + bytes memory _calldata = abi.encodeWithSignature("_burn(address,uint256)", _from, _amount); // nosemgrep: + // sol-style-use-abi-encodecall vm.expectRevert(bytes("")); // Act @@ -364,7 +367,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the burn function reverts to protect against accidentally changing the visibility. function testFuzz_calling_burn_function_reverts(address _caller, address _from, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("burn(address,uint256)", _from, _amount); + bytes memory _calldata = abi.encodeWithSignature("burn(address,uint256)", _from, _amount); // nosemgrep: + // sol-style-use-abi-encodecall vm.expectRevert(bytes("")); // Act diff --git a/packages/contracts-bedrock/test/L2/WETH.t.sol b/packages/contracts-bedrock/test/L2/WETH.t.sol index fa16be1f53cc..84bb138d74fb 100644 --- a/packages/contracts-bedrock/test/L2/WETH.t.sol +++ b/packages/contracts-bedrock/test/L2/WETH.t.sol @@ -10,7 +10,7 @@ import { WETH } from "src/L2/WETH.sol"; contract WETH_Test is CommonTest { /// @dev Tests that the name function returns the correct value. function testFuzz_name_succeeds(string memory _gasPayingTokenName) external { - vm.mockCall(address(l1Block), abi.encodeWithSignature("gasPayingTokenName()"), abi.encode(_gasPayingTokenName)); + vm.mockCall(address(l1Block), abi.encodeCall(l1Block.gasPayingTokenName, ()), abi.encode(_gasPayingTokenName)); assertEq(string.concat("Wrapped ", _gasPayingTokenName), weth.name()); } @@ -18,7 +18,7 @@ contract WETH_Test is CommonTest { /// @dev Tests that the symbol function returns the correct value. function testFuzz_symbol_succeeds(string memory _gasPayingTokenSymbol) external { vm.mockCall( - address(l1Block), abi.encodeWithSignature("gasPayingTokenSymbol()"), abi.encode(_gasPayingTokenSymbol) + address(l1Block), abi.encodeCall(l1Block.gasPayingTokenSymbol, ()), abi.encode(_gasPayingTokenSymbol) ); assertEq(string.concat("W", _gasPayingTokenSymbol), weth.symbol()); diff --git a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol index c3cdf2c58d44..524423292058 100644 --- a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol +++ b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol @@ -49,9 +49,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In assert(l2BlockNumber < gameProxy.l2BlockNumber()); // Mock the state that we want. - vm.mockCall( - address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); // Try to update the anchor state. vm.prank(address(gameProxy)); @@ -66,15 +64,13 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In /// @dev Tests that updating the anchor state fails when the game state is valid but older. function test_tryUpdateAnchorState_validOlderState_fails() public { // Confirm that the anchor state is newer than the game state. - vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); assert(l2BlockNumber >= gameProxy.l2BlockNumber()); // Mock the state that we want. - vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); - vm.mockCall( - address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); // Try to update the anchor state. vm.prank(address(gameProxy)); @@ -93,11 +89,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In assert(l2BlockNumber < gameProxy.l2BlockNumber()); // Mock the state that we want. - vm.mockCall( - address(gameProxy), - abi.encodeWithSelector(gameProxy.status.selector), - abi.encode(GameStatus.CHALLENGER_WINS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS)); // Try to update the anchor state. vm.prank(address(gameProxy)); @@ -118,8 +110,8 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In // Mock the state that we want. vm.mockCall( address(disputeGameFactory), - abi.encodeWithSelector( - disputeGameFactory.games.selector, gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData() + abi.encodeCall( + disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()) ), abi.encode(address(0), 0) ); @@ -146,8 +138,8 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In // Mock the state that we want. vm.mockCall( address(disputeGameFactory), - abi.encodeWithSelector( - disputeGameFactory.games.selector, gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData() + abi.encodeCall( + disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()) ), abi.encode(address(0), 0) ); @@ -168,11 +160,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); // Mock the state that we want. - vm.mockCall( - address(gameProxy), - abi.encodeWithSelector(gameProxy.status.selector), - abi.encode(GameStatus.CHALLENGER_WINS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS)); // Set the anchor state. vm.prank(superchainConfig.guardian()); @@ -190,9 +178,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); // Mock the state that we want. - vm.mockCall( - address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.IN_PROGRESS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.IN_PROGRESS)); // Set the anchor state. vm.prank(superchainConfig.guardian()); @@ -208,9 +194,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In /// @dev Tests that setting the anchor state succeeds. function test_setAnchorState_succeeds() public { // Mock the state that we want. - vm.mockCall( - address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS) - ); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); // Set the anchor state. vm.prank(superchainConfig.guardian()); diff --git a/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol b/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol index 00f3a8b19f9c..88e361a80b45 100644 --- a/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol +++ b/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol @@ -6,6 +6,7 @@ import { Test } from "forge-std/Test.sol"; import { Vm } from "forge-std/Vm.sol"; import { DisputeGameFactory_Init } from "test/dispute/DisputeGameFactory.t.sol"; import { AlphabetVM } from "test/mocks/AlphabetVM.sol"; +import { stdError } from "forge-std/StdError.sol"; // Scripts import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; @@ -186,9 +187,7 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { // PreimageOracle constructor will revert if the challenge period is too large, so we need // to mock the call to pretend this is a bugged implementation where the challenge period // is allowed to be too large. - vm.mockCall( - address(oracle), abi.encodeWithSelector(oracle.challengePeriod.selector), abi.encode(_challengePeriod) - ); + vm.mockCall(address(oracle), abi.encodeCall(IPreimageOracle.challengePeriod, ()), abi.encode(_challengePeriod)); vm.expectRevert(InvalidChallengePeriod.selector); DeployUtils.create1({ @@ -536,11 +535,11 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { Claim claim = _dummyClaim(); // Expect an out of bounds revert for an attack - vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32)); + vm.expectRevert(stdError.indexOOBError); gameProxy.attack(_dummyClaim(), 1, claim); // Expect an out of bounds revert for a defense - vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32)); + vm.expectRevert(stdError.indexOOBError); gameProxy.defend(_dummyClaim(), 1, claim); } @@ -1704,14 +1703,14 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { /// resolves in favor of the defender but the game state is not newer than the anchor state. function test_resolve_validOlderStateSameAnchor_succeeds() public { // Mock the game block to be older than the game state. - vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); // Confirm that the anchor state is newer than the game state. (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); assert(l2BlockNumber >= gameProxy.l2BlockNumber()); // Resolve the game. - vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); + vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); vm.warp(block.timestamp + 3 days + 12 hours); gameProxy.resolveClaim(0, 0); assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); diff --git a/packages/contracts-bedrock/test/invariants/SafeCall.t.sol b/packages/contracts-bedrock/test/invariants/SafeCall.t.sol index 0a70c3ddd906..2de6183bd130 100644 --- a/packages/contracts-bedrock/test/invariants/SafeCall.t.sol +++ b/packages/contracts-bedrock/test/invariants/SafeCall.t.sol @@ -103,10 +103,7 @@ contract SafeCaller_Actor is StdUtils { vm.expectCallMinGas(to, value, minGas, hex""); bool success = SafeCall.call( - msg.sender, - gas, - value, - abi.encodeWithSelector(SafeCall_Succeeds_Invariants.performSafeCallMinGas.selector, to, minGas) + msg.sender, gas, value, abi.encodeCall(SafeCall_Succeeds_Invariants.performSafeCallMinGas, (to, minGas)) ); if (success && FAILS) numCalls++; diff --git a/packages/contracts-bedrock/test/kontrol/proofs/L1ERC721Bridge.k.sol b/packages/contracts-bedrock/test/kontrol/proofs/L1ERC721Bridge.k.sol index 015ee020e7d1..5e45e3e3a9fd 100644 --- a/packages/contracts-bedrock/test/kontrol/proofs/L1ERC721Bridge.k.sol +++ b/packages/contracts-bedrock/test/kontrol/proofs/L1ERC721Bridge.k.sol @@ -34,7 +34,7 @@ contract L1ERC721BridgeKontrol is DeploymentSummaryFaultProofs, KontrolUtils { vm.mockCall( address(l1ERC721Bridge.messenger()), - abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1ERC721Bridge.otherBridge())) ); diff --git a/packages/contracts-bedrock/test/kontrol/proofs/L1StandardBridge.k.sol b/packages/contracts-bedrock/test/kontrol/proofs/L1StandardBridge.k.sol index 6e65f852719b..b5f8793426e6 100644 --- a/packages/contracts-bedrock/test/kontrol/proofs/L1StandardBridge.k.sol +++ b/packages/contracts-bedrock/test/kontrol/proofs/L1StandardBridge.k.sol @@ -34,7 +34,7 @@ contract L1StandardBridgeKontrol is DeploymentSummaryFaultProofs, KontrolUtils { vm.mockCall( address(l1standardBridge.messenger()), - abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1standardBridge.otherBridge())) ); @@ -59,7 +59,7 @@ contract L1StandardBridgeKontrol is DeploymentSummaryFaultProofs, KontrolUtils { vm.mockCall( address(l1standardBridge.messenger()), - abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(CrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1standardBridge.otherBridge())) ); diff --git a/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol b/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol index 84c743c97aa3..5b1f40d55bd8 100644 --- a/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol +++ b/packages/contracts-bedrock/test/legacy/ResolvedDelegateProxy.t.sol @@ -43,14 +43,14 @@ contract ResolvedDelegateProxy_Test is Test { /// @dev Tests that the proxy properly bubbles up returndata when the delegatecall succeeds. function testFuzz_fallback_delegateCallFoo_succeeds(uint256 x) public { - vm.expectCall(address(impl), abi.encodeWithSelector(impl.foo.selector, x)); + vm.expectCall(address(impl), abi.encodeCall(impl.foo, (x))); assertEq(proxy.foo(x), x); } /// @dev Tests that the proxy properly bubbles up returndata when the delegatecall reverts. function test_fallback_delegateCallBar_reverts() public { vm.expectRevert("SimpleImplementation: revert"); - vm.expectCall(address(impl), abi.encodeWithSelector(impl.bar.selector)); + vm.expectCall(address(impl), abi.encodeCall(impl.bar, ())); proxy.bar(); } diff --git a/packages/contracts-bedrock/test/libraries/SafeCall.t.sol b/packages/contracts-bedrock/test/libraries/SafeCall.t.sol index fcb9d3832a12..ffdf07b09b83 100644 --- a/packages/contracts-bedrock/test/libraries/SafeCall.t.sol +++ b/packages/contracts-bedrock/test/libraries/SafeCall.t.sol @@ -122,12 +122,12 @@ contract SafeCall_Test is Test { for (uint64 i = 40_000; i < 100_000; i++) { uint256 snapshot = vm.snapshot(); - // 65_907 is the exact amount of gas required to make the safe call + // 65_922 is the exact amount of gas required to make the safe call // successfully. - if (i < 65_907) { + if (i < 65_922) { assertFalse(caller.makeSafeCall(i, 25_000)); } else { - vm.expectCallMinGas(address(caller), 0, 25_000, abi.encodeWithSelector(caller.setA.selector, 1)); + vm.expectCallMinGas(address(caller), 0, 25_000, abi.encodeCall(caller.setA, (1))); assertTrue(caller.makeSafeCall(i, 25_000)); } @@ -142,12 +142,12 @@ contract SafeCall_Test is Test { for (uint64 i = 15_200_000; i < 15_300_000; i++) { uint256 snapshot = vm.snapshot(); - // 15_278_606 is the exact amount of gas required to make the safe call + // 15_278_621 is the exact amount of gas required to make the safe call // successfully. - if (i < 15_278_606) { + if (i < 15_278_621) { assertFalse(caller.makeSafeCall(i, 15_000_000)); } else { - vm.expectCallMinGas(address(caller), 0, 15_000_000, abi.encodeWithSelector(caller.setA.selector, 1)); + vm.expectCallMinGas(address(caller), 0, 15_000_000, abi.encodeCall(caller.setA, (1))); assertTrue(caller.makeSafeCall(i, 15_000_000)); } @@ -160,11 +160,11 @@ contract SimpleSafeCaller { uint256 public a; function makeSafeCall(uint64 gas, uint64 minGas) external returns (bool) { - return SafeCall.call(address(this), gas, 0, abi.encodeWithSelector(this.makeSafeCallMinGas.selector, minGas)); + return SafeCall.call(address(this), gas, 0, abi.encodeCall(this.makeSafeCallMinGas, (minGas))); } function makeSafeCallMinGas(uint64 minGas) external returns (bool) { - return SafeCall.callWithMinGas(address(this), minGas, 0, abi.encodeWithSelector(this.setA.selector, 1)); + return SafeCall.callWithMinGas(address(this), minGas, 0, abi.encodeCall(this.setA, (1))); } function setA(uint256 _a) external { diff --git a/packages/contracts-bedrock/test/periphery/Transactor.t.sol b/packages/contracts-bedrock/test/periphery/Transactor.t.sol index bfee0ddc3adf..8d183c8d20b7 100644 --- a/packages/contracts-bedrock/test/periphery/Transactor.t.sol +++ b/packages/contracts-bedrock/test/periphery/Transactor.t.sol @@ -41,7 +41,7 @@ contract TransactorTest is Transactor_Initializer { /// @notice Tests CALL, should do a call to target function test_call_succeeds() external { // Initialize call data - bytes memory data = abi.encodeWithSelector(callRecorded.record.selector); + bytes memory data = abi.encodeCall(CallRecorder.record, ()); // Run CALL vm.prank(alice); vm.expectCall(address(callRecorded), 200_000 wei, data); @@ -51,7 +51,7 @@ contract TransactorTest is Transactor_Initializer { /// @notice It should revert if called by non-owner function test_call_unauthorized_reverts() external { // Initialize call data - bytes memory data = abi.encodeWithSelector(callRecorded.record.selector); + bytes memory data = abi.encodeCall(CallRecorder.record, ()); // Run CALL vm.prank(bob); vm.expectRevert("UNAUTHORIZED"); @@ -61,7 +61,7 @@ contract TransactorTest is Transactor_Initializer { /// @notice Deletate call succeeds. function test_delegateCall_succeeds() external { // Initialize call data - bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector); + bytes memory data = abi.encodeCall(Reverter.doRevert, ()); // Run CALL vm.prank(alice); vm.expectCall(address(reverter), data); @@ -71,7 +71,7 @@ contract TransactorTest is Transactor_Initializer { /// @notice It should revert if called by non-owner function test_delegateCall_unauthorized_reverts() external { // Initialize call data - bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector); + bytes memory data = abi.encodeCall(Reverter.doRevert, ()); // Run CALL vm.prank(bob); vm.expectRevert("UNAUTHORIZED"); diff --git a/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol b/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol index 66c2a8d69b19..0e6755b0e63c 100644 --- a/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol +++ b/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol @@ -353,7 +353,7 @@ contract Drippie_Test is Test { // Add in an action cfg.actions[0] = Drippie.DripAction({ target: payable(address(simpleStorage)), - data: abi.encodeWithSelector(SimpleStorage.set.selector, key, value), + data: abi.encodeCall(SimpleStorage.set, (key, value)), value: 0 }); @@ -365,7 +365,7 @@ contract Drippie_Test is Test { vm.prank(drippie.owner()); drippie.status(dripName, Drippie.DripStatus.ACTIVE); - vm.expectCall(address(simpleStorage), 0, abi.encodeWithSelector(SimpleStorage.set.selector, key, value)); + vm.expectCall(address(simpleStorage), 0, abi.encodeCall(SimpleStorage.set, (key, value))); vm.expectEmit(address(drippie)); emit DripExecuted(dripName, dripName, address(this), block.timestamp); @@ -383,7 +383,7 @@ contract Drippie_Test is Test { bytes32 valueOne = bytes32(uint256(3)); actions[0] = Drippie.DripAction({ target: payable(address(simpleStorage)), - data: abi.encodeWithSelector(simpleStorage.set.selector, keyOne, valueOne), + data: abi.encodeCall(SimpleStorage.set, (keyOne, valueOne)), value: 0 }); @@ -391,7 +391,7 @@ contract Drippie_Test is Test { bytes32 valueTwo = bytes32(uint256(5)); actions[1] = Drippie.DripAction({ target: payable(address(simpleStorage)), - data: abi.encodeWithSelector(simpleStorage.set.selector, keyTwo, valueTwo), + data: abi.encodeCall(SimpleStorage.set, (keyTwo, valueTwo)), value: 0 }); @@ -407,9 +407,9 @@ contract Drippie_Test is Test { vm.expectCall(drippie.dripConfigCheckAddress(dripName), drippie.dripConfigCheckParams(dripName)); - vm.expectCall(address(simpleStorage), 0, abi.encodeWithSelector(SimpleStorage.set.selector, keyOne, valueOne)); + vm.expectCall(address(simpleStorage), 0, abi.encodeCall(SimpleStorage.set, (keyOne, valueOne))); - vm.expectCall(address(simpleStorage), 0, abi.encodeWithSelector(SimpleStorage.set.selector, keyTwo, valueTwo)); + vm.expectCall(address(simpleStorage), 0, abi.encodeCall(SimpleStorage.set, (keyTwo, valueTwo))); vm.expectEmit(address(drippie)); emit DripExecuted(dripName, dripName, address(this), block.timestamp); diff --git a/packages/contracts-bedrock/test/periphery/op-nft/Optimist.t.sol b/packages/contracts-bedrock/test/periphery/op-nft/Optimist.t.sol index 912b04ab33f6..2eb2f07e860f 100644 --- a/packages/contracts-bedrock/test/periphery/op-nft/Optimist.t.sol +++ b/packages/contracts-bedrock/test/periphery/op-nft/Optimist.t.sol @@ -130,9 +130,7 @@ contract Optimist_Initializer is Test { /// @notice Mocks the allowlistAttestor to always return true for a given address. function _mockAllowlistTrueFor(address _claimer) internal { vm.mockCall( - address(optimistAllowlist), - abi.encodeWithSelector(OptimistAllowlist.isAllowedToMint.selector, _claimer), - abi.encode(true) + address(optimistAllowlist), abi.encodeCall(OptimistAllowlist.isAllowedToMint, (_claimer)), abi.encode(true) ); assertTrue(optimist.isOnAllowList(_claimer)); @@ -210,7 +208,7 @@ contract OptimistTest is Optimist_Initializer { assertTrue(optimistAllowlist.isAllowedToMint(bob)); // Check that the OptimistAllowlist is checked - bytes memory data = abi.encodeWithSelector(optimistAllowlist.isAllowedToMint.selector, bob); + bytes memory data = abi.encodeCall(OptimistAllowlist.isAllowedToMint, (bob)); vm.expectCall(address(optimistAllowlist), data); // mint an NFT and expect mint transfer event to be emitted @@ -236,7 +234,7 @@ contract OptimistTest is Optimist_Initializer { assertTrue(optimistAllowlist.isAllowedToMint(bob)); // Check that the OptimistAllowlist is checked - bytes memory data = abi.encodeWithSelector(optimistAllowlist.isAllowedToMint.selector, bob); + bytes memory data = abi.encodeCall(OptimistAllowlist.isAllowedToMint, (bob)); vm.expectCall(address(optimistAllowlist), data); // mint an NFT and expect mint transfer event to be emitted @@ -262,7 +260,7 @@ contract OptimistTest is Optimist_Initializer { assertTrue(optimistAllowlist.isAllowedToMint(bob)); // Check that the OptimistAllowlist is checked - bytes memory data = abi.encodeWithSelector(optimistAllowlist.isAllowedToMint.selector, bob); + bytes memory data = abi.encodeCall(OptimistAllowlist.isAllowedToMint, (bob)); vm.expectCall(address(optimistAllowlist), data); // mint an NFT and expect mint transfer event to be emitted @@ -293,7 +291,7 @@ contract OptimistTest is Optimist_Initializer { assertTrue(optimistAllowlist.isAllowedToMint(bob)); // Check that the OptimistAllowlist is checked - bytes memory data = abi.encodeWithSelector(optimistAllowlist.isAllowedToMint.selector, bob); + bytes memory data = abi.encodeCall(OptimistAllowlist.isAllowedToMint, (bob)); vm.expectCall(address(optimistAllowlist), data); // mint an NFT and expect mint transfer event to be emitted @@ -350,11 +348,9 @@ contract OptimistTest is Optimist_Initializer { function test_baseURI_returnsCorrectBaseURI_succeeds() external { _attestBaseURI(base_uri); - bytes memory data = abi.encodeWithSelector( - attestationStation.attestations.selector, - carol_baseURIAttestor, - address(optimist), - optimist.BASE_URI_ATTESTATION_KEY() + bytes memory data = abi.encodeCall( + attestationStation.attestations, + (carol_baseURIAttestor, address(optimist), optimist.BASE_URI_ATTESTATION_KEY()) ); vm.expectCall(address(attestationStation), data); vm.prank(carol_baseURIAttestor); @@ -527,14 +523,14 @@ contract OptimistTest is Optimist_Initializer { // First call is to claim the invite, receiving the attestation calls[0] = IMulticall3.Call3({ target: address(optimistInviter), - callData: abi.encodeWithSelector(optimistInviter.claimInvite.selector, bob, claimableInvite, signature), + callData: abi.encodeCall(OptimistInviter.claimInvite, (bob, claimableInvite, signature)), allowFailure: false }); // Second call is to mint the Optimist NFT calls[1] = IMulticall3.Call3({ target: address(optimist), - callData: abi.encodeWithSelector(optimist.mint.selector, bob), + callData: abi.encodeCall(Optimist.mint, (bob)), allowFailure: false }); diff --git a/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol b/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol index 34386f5e3923..976cae553691 100644 --- a/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol +++ b/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol @@ -260,7 +260,7 @@ library SafeTestLib { instance, address(instance.safe), 0, - abi.encodeWithSelector(ModuleManager.enableModule.selector, module), + abi.encodeCall(ModuleManager.enableModule, (module)), Enum.Operation.Call, 0, 0, @@ -289,7 +289,7 @@ library SafeTestLib { instance, address(instance.safe), 0, - abi.encodeWithSelector(ModuleManager.disableModule.selector, prevModule, module), + abi.encodeCall(ModuleManager.disableModule, (prevModule, module)), Enum.Operation.Call, 0, 0, @@ -308,7 +308,7 @@ library SafeTestLib { instance, address(instance.safe), 0, - abi.encodeWithSelector(GuardManager.setGuard.selector, guard), + abi.encodeCall(GuardManager.setGuard, (guard)), Enum.Operation.Call, 0, 0, @@ -326,7 +326,7 @@ library SafeTestLib { instance: instance, to: signMessageLib, value: 0, - data: abi.encodeWithSelector(SignMessageLib.signMessage.selector, data), + data: abi.encodeCall(SignMessageLib.signMessage, (data)), operation: Enum.Operation.DelegateCall, safeTxGas: 0, baseGas: 0, @@ -350,21 +350,13 @@ library SafeTestLib { /// @dev Adds a new owner to the safe function changeThreshold(SafeInstance memory instance, uint256 threshold) internal { - execTransaction( - instance, - address(instance.safe), - 0, - abi.encodeWithSelector(OwnerManager.changeThreshold.selector, threshold) - ); + execTransaction(instance, address(instance.safe), 0, abi.encodeCall(OwnerManager.changeThreshold, (threshold))); } /// @dev Adds a new owner to the safe function addOwnerWithThreshold(SafeInstance memory instance, address owner, uint256 threshold) internal { execTransaction( - instance, - address(instance.safe), - 0, - abi.encodeWithSelector(OwnerManager.addOwnerWithThreshold.selector, owner, threshold) + instance, address(instance.safe), 0, abi.encodeCall(OwnerManager.addOwnerWithThreshold, (owner, threshold)) ); } @@ -373,10 +365,7 @@ library SafeTestLib { function removeOwner(SafeInstance memory instance, address prevOwner, address owner, uint256 threshold) internal { prevOwner = prevOwner > address(0) ? prevOwner : SafeTestLib.getPrevOwner(instance, owner); execTransaction( - instance, - address(instance.safe), - 0, - abi.encodeWithSelector(OwnerManager.removeOwner.selector, prevOwner, owner, threshold) + instance, address(instance.safe), 0, abi.encodeCall(OwnerManager.removeOwner, (prevOwner, owner, threshold)) ); } @@ -385,10 +374,7 @@ library SafeTestLib { function swapOwner(SafeInstance memory instance, address prevOwner, address oldOwner, address newOwner) internal { prevOwner = prevOwner > address(0) ? prevOwner : SafeTestLib.getPrevOwner(instance, oldOwner); execTransaction( - instance, - address(instance.safe), - 0, - abi.encodeWithSelector(OwnerManager.swapOwner.selector, prevOwner, oldOwner, newOwner) + instance, address(instance.safe), 0, abi.encodeCall(OwnerManager.swapOwner, (prevOwner, oldOwner, newOwner)) ); } @@ -537,16 +523,18 @@ contract SafeTestTools { bytes memory initData = advancedParams.initData.length > 0 ? advancedParams.initData - : abi.encodeWithSelector( - GnosisSafe.setup.selector, - owners, - threshold, - advancedParams.setupModulesCall_to, - advancedParams.setupModulesCall_data, - advancedParams.includeFallbackHandler ? address(handler) : address(0), - advancedParams.refundToken, - advancedParams.refundAmount, - advancedParams.refundReceiver + : abi.encodeCall( + GnosisSafe.setup, + ( + owners, + threshold, + advancedParams.setupModulesCall_to, + advancedParams.setupModulesCall_data, + advancedParams.includeFallbackHandler ? address(handler) : address(0), + advancedParams.refundToken, + advancedParams.refundAmount, + advancedParams.refundReceiver + ) ); DeployedSafe safe0 = DeployedSafe( diff --git a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol index 2fec587211f7..8dd1ba970abd 100644 --- a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol +++ b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol @@ -100,7 +100,7 @@ contract DeputyGuardianModule_Pause_TestFail is DeputyGuardianModule_TestInit { function test_pause_targetReverts_reverts() external { vm.mockCallRevert( address(superchainConfig), - abi.encodeWithSelector(superchainConfig.pause.selector), + abi.encodePacked(superchainConfig.pause.selector), "SuperchainConfig: pause() reverted" ); @@ -150,7 +150,7 @@ contract DeputyGuardianModule_Unpause_TestFail is DeputyGuardianModule_Unpause_T function test_unpause_targetReverts_reverts() external { vm.mockCallRevert( address(superchainConfig), - abi.encodeWithSelector(superchainConfig.unpause.selector), + abi.encodePacked(superchainConfig.unpause.selector), "SuperchainConfig: unpause reverted" ); @@ -170,9 +170,7 @@ contract DeputyGuardianModule_SetAnchorState_TestFail is DeputyGuardianModule_Te function test_setAnchorState_targetReverts_reverts() external { IAnchorStateRegistry asr = IAnchorStateRegistry(makeAddr("asr")); vm.mockCallRevert( - address(asr), - abi.encodeWithSelector(asr.setAnchorState.selector), - "AnchorStateRegistry: setAnchorState reverted" + address(asr), abi.encodePacked(asr.setAnchorState.selector), "AnchorStateRegistry: setAnchorState reverted" ); vm.prank(address(deputyGuardian)); vm.expectRevert( @@ -186,9 +184,7 @@ contract DeputyGuardianModule_SetAnchorState_Test is DeputyGuardianModule_TestIn function test_setAnchorState_succeeds() external { IAnchorStateRegistry asr = IAnchorStateRegistry(makeAddr("asr")); vm.mockCall( - address(asr), - abi.encodeWithSelector(IAnchorStateRegistry.setAnchorState.selector, IFaultDisputeGame(address(0))), - "" + address(asr), abi.encodeCall(IAnchorStateRegistry.setAnchorState, (IFaultDisputeGame(address(0)))), "" ); vm.expectEmit(address(safeInstance.safe)); emit ExecutionFromModuleSuccess(address(deputyGuardianModule)); @@ -228,7 +224,7 @@ contract DeputyGuardianModule_BlacklistDisputeGame_TestFail is DeputyGuardianMod function test_blacklistDisputeGame_targetReverts_reverts() external { vm.mockCallRevert( address(optimismPortal2), - abi.encodeWithSelector(optimismPortal2.blacklistDisputeGame.selector), + abi.encodePacked(optimismPortal2.blacklistDisputeGame.selector), "OptimismPortal2: blacklistDisputeGame reverted" ); @@ -271,7 +267,7 @@ contract DeputyGuardianModule_setRespectedGameType_TestFail is DeputyGuardianMod function test_setRespectedGameType_targetReverts_reverts() external { vm.mockCallRevert( address(optimismPortal2), - abi.encodeWithSelector(optimismPortal2.setRespectedGameType.selector), + abi.encodePacked(optimismPortal2.setRespectedGameType.selector), "OptimismPortal2: setRespectedGameType reverted" ); diff --git a/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol b/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol index f1386a6608a2..68ddddec5b37 100644 --- a/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol +++ b/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol @@ -110,7 +110,7 @@ contract LivenessGuard_CheckTx_Test is LivenessGuard_TestInit { vm.expectEmit(address(livenessGuard)); emit OwnerRecorded(signers[i]); } - vm.expectCall(address(safeInstance.safe), abi.encodeWithSignature("nonce()")); + vm.expectCall(address(safeInstance.safe), abi.encodeCall(safeInstance.safe.nonce, ())); vm.expectCall(address(safeInstance.safe), abi.encodeCall(OwnerManager.getThreshold, ())); safeInstance.execTransaction({ to: address(1111), value: 0, data: hex"abba" }); for (uint256 i; i < safeInstance.threshold; i++) { @@ -228,6 +228,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness mapping(address => uint256) privateKeys; /// @dev Tests that the guard correctly manages the lastLive mapping when owners are added, removed, or swapped + /// forge-config: ciheavy.fuzz.runs = 8192 function testFuzz_OwnerManagement_works( uint256 initialOwners, uint256 threshold, diff --git a/packages/contracts-bedrock/test/safe/SafeSigners.t.sol b/packages/contracts-bedrock/test/safe/SafeSigners.t.sol index 9cfa91869899..cb9737a3e975 100644 --- a/packages/contracts-bedrock/test/safe/SafeSigners.t.sol +++ b/packages/contracts-bedrock/test/safe/SafeSigners.t.sol @@ -75,6 +75,7 @@ contract SafeSigners_Test is Test, SafeTestTools { contractSigs++; address addr = SafeTestLib.decodeSmartContractWalletAsAddress(pks[i]); r = bytes32(uint256(uint160(addr))); + // nosemgrep: sol-style-use-abi-encodecall vm.mockCall( addr, abi.encodeWithSignature("isValidSignature(bytes,bytes)"), abi.encode(EIP1271_MAGIC_VALUE) ); diff --git a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol index 454158566f6c..a68cccf6862f 100644 --- a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol @@ -187,7 +187,7 @@ contract GasBenchMark_L1StandardBridge_Finalize is Bridge_Initializer { deal(address(L1Token), address(l1StandardBridge), 100, true); vm.mockCall( address(l1StandardBridge.messenger()), - abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), abi.encode(address(l1StandardBridge.OTHER_BRIDGE())) ); vm.startPrank(address(l1StandardBridge.messenger())); @@ -302,7 +302,7 @@ contract GasBenchMark_L1BlockInterop_DepositsComplete is GasBenchMark_L1BlockInt function test_depositsComplete_benchmark() external { SafeCall.call({ _target: address(l1BlockInterop), - _calldata: abi.encodeWithSelector(l1BlockInterop.depositsComplete.selector) + _calldata: abi.encodeCall(IL1BlockInterop.depositsComplete, ()) }); } } @@ -317,7 +317,7 @@ contract GasBenchMark_L1BlockInterop_DepositsComplete_Warm is GasBenchMark_L1Blo function test_depositsComplete_benchmark() external { SafeCall.call({ _target: address(l1BlockInterop), - _calldata: abi.encodeWithSelector(l1BlockInterop.depositsComplete.selector) + _calldata: abi.encodeCall(l1BlockInterop.depositsComplete, ()) }); } } diff --git a/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol index dddb09d41239..065c3e1bc3fc 100644 --- a/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol @@ -99,7 +99,7 @@ contract ExternalRelay is Test { /// @notice Helper function to get the callData for an `externalCallWithMinGas function getCallData() public pure returns (bytes memory) { - return abi.encodeWithSelector(ExternalRelay.externalCallWithMinGas.selector); + return abi.encodeCall(ExternalRelay.externalCallWithMinGas, ()); } /// @notice Helper function to set the fuzzed sender diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol index cba5fc829086..d146b050f387 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol @@ -42,7 +42,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer { vm.startPrank(EIP1967Helper.getAdmin(address(proxy))); // Reviewer note: the NextImpl() still uses reinitializer. If we want to remove that, we'll need to use a // two step upgrade with the Storage lib. - proxy.upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2)); + proxy.upgradeToAndCall(address(nextImpl), abi.encodeCall(NextImpl.initialize, (2))); assertEq(proxy.implementation(), address(nextImpl)); // Verify that the NextImpl contract initialized its values according as expected diff --git a/packages/contracts-bedrock/test/universal/Proxy.t.sol b/packages/contracts-bedrock/test/universal/Proxy.t.sol index 9df4e1799aa0..8c6aa7ae5137 100644 --- a/packages/contracts-bedrock/test/universal/Proxy.t.sol +++ b/packages/contracts-bedrock/test/universal/Proxy.t.sol @@ -160,7 +160,7 @@ contract Proxy_Test is Test { vm.expectEmit(true, true, true, true); emit Upgraded(address(simpleStorage)); vm.prank(alice); - proxy.upgradeToAndCall(address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1)); + proxy.upgradeToAndCall(address(simpleStorage), abi.encodeCall(SimpleStorage.set, (1, 1))); // The call should have impacted the state uint256 result = SimpleStorage(address(proxy)).get(1); @@ -193,7 +193,7 @@ contract Proxy_Test is Test { // The attempt to `upgradeToAndCall` // should revert when it is not called by the owner. vm.expectRevert(bytes("")); - proxy.upgradeToAndCall(address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1)); + proxy.upgradeToAndCall(address(simpleStorage), abi.encodeCall(simpleStorage.set, (1, 1))); } function test_upgradeToAndCall_isPayable_succeeds() external { @@ -202,9 +202,7 @@ contract Proxy_Test is Test { // Set the implementation and call and send // value. vm.prank(alice); - proxy.upgradeToAndCall{ value: 1 ether }( - address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1) - ); + proxy.upgradeToAndCall{ value: 1 ether }(address(simpleStorage), abi.encodeCall(simpleStorage.set, (1, 1))); // The implementation address should be correct vm.prank(alice); @@ -265,7 +263,8 @@ contract Proxy_Test is Test { (bool success, bytes memory returndata) = address(proxy).call(hex""); assertEq(success, false); - bytes memory err = abi.encodeWithSignature("Error(string)", "Proxy: implementation not initialized"); + bytes memory err = abi.encodeWithSignature("Error(string)", "Proxy: implementation not initialized"); // nosemgrep: + // sol-style-use-abi-encodecall assertEq(returndata, err); } diff --git a/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol b/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol index e6d6ced1f971..04e416cbd3a9 100644 --- a/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol +++ b/packages/contracts-bedrock/test/universal/ProxyAdmin.t.sol @@ -248,7 +248,7 @@ contract ProxyAdmin_Test is Test { function upgradeAndCall(address payable _proxy) internal { vm.prank(alice); - admin.upgradeAndCall(_proxy, address(implementation), abi.encodeWithSelector(SimpleStorage.set.selector, 1, 1)); + admin.upgradeAndCall(_proxy, address(implementation), abi.encodeCall(SimpleStorage.set, (1, 1))); address impl = admin.getProxyImplementation(_proxy); assertEq(impl, address(implementation)); diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 71df6566d4c5..bd9a80d57804 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -54,7 +54,8 @@ rules: version\(\) public pure virtual returns \(string memory\) \{\s+return "(?P[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.]+)?)"; - - pattern-regex: /// @custom:semver (?P[a-zA-Z0-9.+-]+)\s+function + - pattern-regex: + /// @custom:semver (?P[a-zA-Z0-9.+-]+)\s+function version\(\) public pure override returns \(string memory\) \{\s+return string\.concat\(super\.version\(\), "(?P[a-zA-Z0-9.+-]+)"\); @@ -152,11 +153,14 @@ rules: severity: ERROR message: Use abi.encodeCall instead of abi.encodeWithSelector patterns: - - pattern: | - abi.encodeWithSelector(...); + - pattern-either: + - pattern: | + abi.encodeWithSelector(...); + - pattern: | + abi.encodeWithSignature(...); + - pattern-not: vm.expectRevert(abi.encodeWithSelector(...)); paths: exclude: - - packages/contracts-bedrock/test - packages/contracts-bedrock/src/L1/OPContractsManager.sol - packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol - packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol From 654c76251c4f212e27a23073f020e6a65fb7bcd7 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Mon, 28 Oct 2024 15:11:57 +0100 Subject: [PATCH 053/451] rm changelog (#12697) --- packages/contracts-bedrock/CHANGELOG.md | 491 ------------------------ 1 file changed, 491 deletions(-) delete mode 100644 packages/contracts-bedrock/CHANGELOG.md diff --git a/packages/contracts-bedrock/CHANGELOG.md b/packages/contracts-bedrock/CHANGELOG.md deleted file mode 100644 index e4d02f372ade..000000000000 --- a/packages/contracts-bedrock/CHANGELOG.md +++ /dev/null @@ -1,491 +0,0 @@ -# @eth-optimism/contracts-bedrock - -## 0.17.3 - -### Patch Changes - -- [#10621](https://github.com/ethereum-optimism/optimism/pull/10621) [`eb454ac72b26211eb8037e7e777315c8f30d994d`](https://github.com/ethereum-optimism/optimism/commit/eb454ac72b26211eb8037e7e777315c8f30d994d) Thanks [@tarunkhasnavis](https://github.com/tarunkhasnavis)! - Add data field to faucet contract drip parameters - -## 0.17.2 - -### Patch Changes - -- [#9964](https://github.com/ethereum-optimism/optimism/pull/9964) [`8241220898128e1f61064f22dcb6fdd0a5f043c3`](https://github.com/ethereum-optimism/optimism/commit/8241220898128e1f61064f22dcb6fdd0a5f043c3) Thanks [@roninjin10](https://github.com/roninjin10)! - Removed only-allow command from package.json - -## 0.17.1 - -### Patch Changes - -- [#9415](https://github.com/ethereum-optimism/optimism/pull/9415) [`79effc52e8b82d15b5eda43acf540ac6c5f8d5d7`](https://github.com/ethereum-optimism/optimism/commit/79effc52e8b82d15b5eda43acf540ac6c5f8d5d7) Thanks [@nitaliano](https://github.com/nitaliano)! - Bumps version so fpac contracts exist for SDK to consume - -## 0.16.2 - -### Patch Changes - -- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures - -## 0.16.1 - -### Patch Changes - -- [#7244](https://github.com/ethereum-optimism/optimism/pull/7244) [`2440f5e7a`](https://github.com/ethereum-optimism/optimism/commit/2440f5e7ab6577f2d2e9c8b0c78c014290dde8e7) Thanks [@nitaliano](https://github.com/nitaliano)! - bumps sdk version to have access to sepolia deployments - -## 0.16.0 - -### Minor Changes - -- [#6206](https://github.com/ethereum-optimism/optimism/pull/6206) [`72d184854`](https://github.com/ethereum-optimism/optimism/commit/72d184854ebad8b2025641f126ed76573b1f0ac3) Thanks [@tynes](https://github.com/tynes)! - Migrate contracts periphery into bedrock - -### Patch Changes - -- [#6164](https://github.com/ethereum-optimism/optimism/pull/6164) [`c11039060`](https://github.com/ethereum-optimism/optimism/commit/c11039060bc037a88916c2cba602687b6d69ad1a) Thanks [@pengin7384](https://github.com/pengin7384)! - fix typo - -- [#6198](https://github.com/ethereum-optimism/optimism/pull/6198) [`77da6edc6`](https://github.com/ethereum-optimism/optimism/commit/77da6edc643e0b5e39f7b6bb41c3c7ead418a876) Thanks [@tremarkley](https://github.com/tremarkley)! - Delete dead typescript https://github.com/ethereum-optimism/optimism/pull/6148. - -## 0.15.0 - -### Minor Changes - -- af292562f: Fix issue with deposits running out of gas - -### Patch Changes - -- Updated dependencies [8d7dcc70c] -- Updated dependencies [d6388be4a] - - @eth-optimism/core-utils@0.12.1 - -## 0.14.0 - -### Minor Changes - -- 197884eae: Bump XDM semver after #5444 -- 6eb05430d: Increase precision in `SafeCall.hasMinGas` -- 5063a69fb: Update sdk contract addresses for bedrock - -### Patch Changes - -- f1e867177: contracts-bedrock was exporting hardhat when it didn't need to be - -## 0.13.2 - -### Patch Changes - -- b16067a9f: Reduce the time that the system dictator deploy scripts wait before checking the chain state. -- 9a02079eb: Makes the Proxy contract inheritable by making functions (public virtual). -- 98fbe9d22: Added a contsructor to the System Dictator - -## 0.13.1 - -### Patch Changes - -- 22c3885f5: Optionally print cast commands during migration -- f52c07529: Print tenderly simulation links during deployment - -## 0.13.0 - -### Minor Changes - -- cb19e2f9c: Moves `FINALIZATION_PERIOD_SECONDS` from the `OptimismPortal` to the `L2OutputOracle` & ensures the `CHALLENGER` key cannot delete finalized outputs. - -## 0.12.1 - -### Patch Changes - -- 80f2271f5: Update foundry - -## 0.12.0 - -### Minor Changes - -- efc98d261: Change the `relayMessage` reentrancy guard in the XDMs to be per-message. - -### Patch Changes - -- 7c0a2cc37: add is IERC165 to IOptimismMintableERC20 -- 2865dd9b4: Minor comment updates and cleanup to the SystemConfig contract. -- 388f2c25a: Trigger a release including CrossDomainOwnable3 - -## 0.11.4 - -### Patch Changes - -- 3c22333b8: Loosens the requirements for re-proving a withdrawal transaction in the `OptimismPortal` - -## 0.11.3 - -### Patch Changes - -- 4964be480: Added a test for large deposit gaps - -## 0.11.2 - -### Patch Changes - -- 8784bc0bc: Add invariant test for the L1 XDM's `relayMessage` minimum gas limits. - -## 0.11.1 - -### Patch Changes - -- fe80a9488: Add echidna tests for portal -- 827fc7b04: Adds a go package to generate fuzz inputs for the Bedrock contract tests. -- a2166dcad: Add echidna tests for metering -- ff09ec22d: Add echidna tests for hashing -- 85dfa9fe2: Add echidna tests for encoding -- 0f8fc58ad: Add echidna tests for Burn -- 89f70c591: Add tests for the `Bytes` library -- 03940c3cb: Make lint check and fix scripts match - -## 0.11.0 - -### Minor Changes - -- 4d13f0afe: Refactors the MerkleTrie get function to throw explicitly instead of returning an existence boolean - -### Patch Changes - -- 43f33f39f: Add echidna test commands -- 237a351f1: Add tests to the SystemConfig contract -- 1d3c749a2: Bumps the version of ts-node used -- 1594678e0: Add echidna test for AliasHelper -- 1d3c749a2: Updates the version of TypeScript -- 136ea1785: Refactors the L2OutputOracle to key the l2Outputs mapping by index instead of by L2 block number. -- 7300a7ca7: Document test function naming convention and create a script for checking. -- Updated dependencies [c975c9620] -- Updated dependencies [136ea1785] - - @eth-optimism/core-utils@0.12.0 - -## 0.10.0 - -### Minor Changes - -- 59adcaa09: Deleted Unused Variables fundAccount , impersonatedTx -- 1bfe79f20: Adds an implementation of the Two Step Withdrawals V2 proposal - -### Patch Changes - -- c025a1153: Fixes a severe vulnerability found in ToB's November 2022 audit of the Bedrock contracts -- f8697a607: Removes historicalTotalBlocks from the L2OutputOracle -- c71500a7e: Updates L2OutputOracle to easily delete multiple outputs at once -- f49b71d50: Updated forge-std version -- ccaf5bc83: Allows owner and proposer addresses to be the same in L2OutputOracle - -## 0.9.1 - -### Patch Changes - -- 52079cc12: Has ProxyAdmin use Ownable instead of Owned -- 13bfafb21: Tweaks variable ordering in OptimismPortal -- eeae96941: Removes the unused DeployConfig contract -- 427831d86: Add comments to SystemConfig.sol - -## 0.9.0 - -### Minor Changes - -- 87702c741: Use SCREAMING_SNAKE_CASE for immutables - -### Patch Changes - -- c02831144: Introduces layout lock and more storage layout verification -- d58b0a397: Cleans up remaining lint warnings -- ff860ecf3: Introduces the MigrationSystemDictator for the Bedrock migration -- cc5adbc61: Updates function ordering in ProxyAdmin to match original contract -- 31c91ea74: Adds another assertion in the CrossDomainMessenger to explicitly identify an invariant which was previously implicit. -- Updated dependencies [1e76cdb86] - - @eth-optimism/core-utils@0.11.0 - -## 0.8.3 - -### Patch Changes - -- db84317b: Various RLP updates -- 9b90c732: Added codecov badge to readme - -## 0.8.2 - -### Patch Changes - -- 7d7d9ba8: Moves initializers underneath constructors always - -## 0.8.1 - -### Patch Changes - -- 35a7bb5e: Use uint64 for arithmetic in XDM's baseGas -- a5e715c3: Rename the event emitted in the L2ToL1MessagePasser -- d18b8aa3: Removes an unnecessary initializer parameter in the L200 - -## 0.8.0 - -### Minor Changes - -- 3d4e8529: No refunds! - -### Patch Changes - -- 6ed68fa3: Fixes a small bug in the constructor of the L2OutputOracle contract -- caf5dd3e: Updates README to include versioning rules. -- a6cbfee2: Fuzz L2ToL1MessagePasser -- 394a26ec: Modifies the StandardBridge to move a value check deeper down the call stack to be more defensive. - -## 0.7.0 - -### Minor Changes - -- e2faaa8b: Moves the L2ToL1MessagePasser to a new address and puts a LegacyMessagePasser at the old address. - -### Patch Changes - -- cb5fed67: Clarify intent with mintable token interfaces -- c427f0c0: Fixes to natspec docs -- d28ad592: Tweaks storage spacers to standardize names and use original types -- 76c8ee2d: Fix event names orderings for `OptimismMintableERC20Created` - -## 0.6.3 - -### Patch Changes - -- 88dde7c8: Uses assert rather than a require statements to check for conditions we believe are unreachable.This is more semantically explicit, and should enable us to more effectively use some advanced analysis methods in our testing. -- 7215f4ce: Bump ethers to 5.7.0 globally -- 249a8ed6: Fixed a backwards compatibility issue in which incorrect events were emitted during a failed deposit finalization on the L2 bridge. -- 7d7c4fdf: Makes spacers private and updates names to reflect slot, offset, and length. -- e164e22e: Makes finalizeWithdrawalTransaction not payable because it doesn't need to be and it was causing confusion throughout the codebase. -- 0bc1be45: Make the use of storage gaps consistent across contracts -- af3e56b1: Fix to Ensure the Oracle's owner and proposer accounts are unique -- 206f6033: Fix outdated references to 'withdrawal contract' -- 88dde7c8: Use assert statements for unreachable conditions. -- 8790156c: Simplifies the initialization function of the CrossDomainMessenger in Bedrock -- 515685f4: Update comments on GovernanceToken to match Seaport style. -- Updated dependencies [7215f4ce] -- Updated dependencies [206f6033] - - @eth-optimism/core-utils@0.10.1 - -## 0.6.2 - -### Patch Changes - -- 651a2883: Make spacer variables private in the Bridge and Messenger contracts so that they cannot be accessed in child contracts. - -## 0.6.1 - -### Patch Changes - -- 85232179: Add CrossDomainOwnable contracts -- 593f1cfb: Removes the blockedSystemMessages mapping in favor of a simpler approach to preventing messages from being sent to internal system addresses. -- f78eb056: Prevents v0 (legacy) messages from being relayed in the bedrock XDM. - -## 0.6.0 - -### Minor Changes - -- 7fdc490c: Removes initializer from StandardBridge in favor of immutables -- 3d228a0e: Updates the storage layout for the CrossDomainMessenger base contract to reduce diff with the existing system. - -### Patch Changes - -- 63ef1949: Delete hardhat genesis tasks -- Updated dependencies [dbfea116] - - @eth-optimism/core-utils@0.10.0 - -## 0.5.4 - -### Patch Changes - -- a095d544: Include latest devnet deployment artifacts -- cdf2163e: Bump oz packages to latest release -- 791f30bc: Migrate deploy config to json from ts -- 193befed: Fix nonce issue for parallel deployments -- 02420db0: Add missing predeploy to Predeploys.sol -- 94a8f287: Moves forge-std and ds-test to devDependencies to avoid breaking npm -- 7d03c5c0: Update the L2 genesis hardhat task to use the ProxyAdmin's deployed address as the admin of each predeploy -- fec22bfe: Fix legibility in the L2CrossDomainMessengerInitializer -- 9272253e: Make a library call internal -- c025f418: Add additional deployments of address manager and proxy admin -- 329d21b6: Use safecall that doesn't copy calldata -- 35eafed0: Deletes the L2 genesis creation hardhat task as its now replaced by go code -- 3cde9205: Update @foundry-rs/hardhat-forge to 0.1.17 - -## 0.5.3 - -### Patch Changes - -- 056cb982: Fix slither script -- a32e68ac: Update genesis-l2 task to set immutables in the bytecode -- c648d55c: Fix build for smaller package -- d544f804: Use the same initializable across codebase -- ccbfe545: Update @foundry-rs/hardhat-forge@0.1.16 -- c97ad241: Fix build on latest foundry -- 45541553: Emit an extra event when withdrawals are initiated to make chainops easier -- 3dd296e8: Fix portal deployment to have L2OutputOracle proxy address -- fe94b864: Add watch task -- 28649d64: Add harhdat forge contract verification support -- 898c7ac5: Update hardhat-forge dep, remove dead deps -- 51a1595b: bedrock-goerli-96f44f79 deployment -- 8ae39154: Update deposit transaction type -- af96563a: Fix typechain exports -- dac4a9f0: Updates the SDK to be compatible with Bedrock (via the "bedrock: true" constructor param). Updates the build pipeline for contracts-bedrock to export a properly formatted dist folder that matches our other packages. -- Updated dependencies [0df744f6] -- Updated dependencies [8ae39154] -- Updated dependencies [dac4a9f0] - - @eth-optimism/core-utils@0.9.3 - -## 0.5.2 - -### Patch Changes - -- 1a22e822: Standardizes revert strings globally -- 5e113137: Fixes a bug in the L2 Bedrock genesis script -- 177a9ea8: Cleans linting errors in MerkleTrie.sol -- 7d68f82f: Adds a new event SentMessageExtension1 to the CrossDomainMessenger contract. Includes additional data that's being attached to messages sent after the Bedrock upgrade. -- 90630336: Properly generates and exports ABI and artifact files that can be imported by client libraries -- 8bd7abde: Moves various legacy contracts into the legacy folder -- 7e6eb9b2: The output oracle's getL2Output function now reverts when no output is returned -- f243dacf: Bump to use solidity 0.8.15 -- 8d26459b: Remove subversion byte from deposit tx -- fa9823f3: Naming improvements for functions and variables in the L2OutputOracle -- 0bf3b9b4: Update forge-std -- e764cbb7: Shortens library names -- 3a0271f8: Introduces Types.sol -- 5de373ea: Semver contract updated to include a getter for the full version string -- Updated dependencies [0bf3b9b4] -- Updated dependencies [8d26459b] -- Updated dependencies [4477fe9f] - - @eth-optimism/core-utils@0.9.2 - -## 0.5.1 - -### Patch Changes - -- e4693481: Clean up BytesUtils -- b7b77d6c: Updates CrossDomainMessenger.baseGas to more accurately reflect gas costs -- 9d435aec: Cleans up natspec in MerkleTrie and SecureMerkleTrie contracts -- 87f745b5: Cleans up various compiler warnings -- 8a3074ab: Minor cleanups to initialization and semver for L1 contracts -- e1501bc0: Clears most contract linting warnings - -## 0.5.0 - -### Minor Changes - -- 42a4cc30: Remove Lib* and OVM* prefixes from all contracts - -### Patch Changes - -- 0cb3929e: Move encoding and hashing into Encoding and Hashing libraries -- 28bd76ae: Cleans up hashing and encoding library natspec and function names -- 4279647f: Port RLPWriter tests -- ce6cb121: Use external version of ExcessivelySafeCall -- 8986f165: Fix solc warnings in ProxyAdmin -- 69ee689f: Remove unnecessary DefaultValues library -- 2e89f634: Fixes a bug that caused L2 timestamps to be computed incorrectly -- 49d33b08: Standardizes comments, errors, and events for contracts in the /universal package -- 821907e2: Bump typechain to 8.1.0 -- 91b31168: Clean up comments and errors for legacy contracts -- 3c5726d4: Cleaned up enums, should be CapitalCase enums and UPPER_CASE values -- eb11a5bb: Add comments to RLP libraries -- 092b0901: Update to new L2 tx hash style for deposits -- 4ea33e13: Standardizes initialization logic for L1 contracts -- 297af083: Move contracts written by external parties into a vendor folder -- 71800503: Reduce the number of compiler warnings -- 611d93a1: Remove storage slot buffer in xdomain messengers -- 75089d0a: Cleans up initialization logic everywhere -- b9a90f32: Rename OptimismMintableTokenFactory to OptimismMintableERC20Factory -- 50e20ea1: Fix initialization logic -- 6f74ca9f: Clean up the PredeployAddresses library -- c031ec95: Tests for RLPReader -- 9c8b1f00: Bump forge-std to 62caef29b0f87a2c6aaaf634b2ca4c09b6867c92 -- 89d01f2e: Add semver to L2 contracts -- 7d9820b6: Resolve compiler warnings in Proxy.sol -- f9fee446: Move the `DepositTx` type to `core-utils`. This way it can be more easily used across projects -- 5050e0fb: Remove "not implemented" errors in virtual functions -- 78d7c2ec: Update typechain pipeline -- 89d01f2e: Update dev deps -- Updated dependencies [f9fee446] - - @eth-optimism/core-utils@0.9.1 - -## 0.4.1 - -### Patch Changes - -- 5c3b4bfa: Enable hardhat style buildinfo -- ef29d8a5: Make the Portal upgradeable -- 5bb6f2c7: Add `OptimismPortal.isOutputFinalized` -- 79f31007: correct l33t sp34k in toCodeAddrr -- 5a12c635: Add deployer docker image -- 8460865f: Optimize buildinfo support, only build through hardhat interface - -## 0.4.0 - -### Minor Changes - -- a828da9f: Add separate sequencer role to Oracle - -### Patch Changes - -- a828da9f: Separate the owner and sequencer roles in the OutputOracle -- 347fd37c: Fix bug in bedrock deploy scripts -- 700dcbb0: Add genesis script -- 931e517b: Fix order of args to L2OO constructor -- 93e2f750: Fix for incorrect constructor args in deploy config -- ddf515cb: Make the output oracle upgradeable. -- Updated dependencies [700dcbb0] - - @eth-optimism/core-utils@0.9.0 - -## 0.3.0 - -### Minor Changes - -- 35757456: Replaces L2 timestamps with block numbers as the key in mapping(uint => OutputProposal). - -### Patch Changes - -- f23bae0b: bedrock: ProxyAdmin rename OpenZeppelin proxy to ERC1967 -- fadb1a93: OZ Audit fixes with a Low or informational severity: - - - Hardcode constant values - - Require that msg.value == \_amount on ETH withdrawals - - use \_from in place of msg.sender when applicable in internal functions - -- f23bae0b: bedrock: Simplify ProxyAdmin static calls -- 650ca6d4: Fixes to medium severity OZ findings - - - Disallow reentrant withdrawals - - remove donateEth - - Correct ordering of \_from and \_to arguments on refunds of failed deposits - -- 9aa8049c: Have contracts-bedrock properly include contract sources in npm package - -## 0.2.0 - -### Minor Changes - -- 04884132: Corrects the ordering of token addresses when a finalizeBridgeERC20 call fails - -### Patch Changes - -- 0a5ca8bf: Deployment for bedrock contracts on goerli -- 2f3fae0e: Fix hh artifact schema -- a96cbe7c: Fix style for L2 contracts to match L1 contracts -- 29ff7462: Revert es target back to 2017 -- 14dd80f3: Add proxy contract -- Updated dependencies [29ff7462] - - @eth-optimism/core-utils@0.8.7 - -## 0.1.3 - -### Patch Changes - -- c258acd4: Update comments and style for L1 contracts - -## 0.1.2 - -### Patch Changes - -- 07a84aed: Move core-utils to deps instead of devdeps - -## 0.1.1 - -### Patch Changes - -- 1aca58c4: Initial release From 4c015e3a36f8910e2cf8b447d62ab4c44b944cca Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Mon, 28 Oct 2024 15:12:10 +0100 Subject: [PATCH 054/451] put deploy scripts in deploy folder (#12694) --- .../scripts/{utils => deploy}/BaseDeployIO.sol | 0 packages/contracts-bedrock/scripts/deploy/Deploy.s.sol | 4 ++-- .../scripts/{ => deploy}/DeployAuthSystem.s.sol | 0 .../scripts/{ => deploy}/DeployImplementations.s.sol | 2 +- .../scripts/{ => deploy}/DeployOPChain.s.sol | 2 +- .../scripts/{ => deploy}/DeploySuperchain.s.sol | 2 +- packages/contracts-bedrock/test/L1/OPContractsManager.t.sol | 2 +- packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol | 2 +- .../contracts-bedrock/test/opcm/DeployImplementations.t.sol | 2 +- packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol | 6 +++--- packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename packages/contracts-bedrock/scripts/{utils => deploy}/BaseDeployIO.sol (100%) rename packages/contracts-bedrock/scripts/{ => deploy}/DeployAuthSystem.s.sol (100%) rename packages/contracts-bedrock/scripts/{ => deploy}/DeployImplementations.s.sol (99%) rename packages/contracts-bedrock/scripts/{ => deploy}/DeployOPChain.s.sol (99%) rename packages/contracts-bedrock/scripts/{ => deploy}/DeploySuperchain.s.sol (99%) diff --git a/packages/contracts-bedrock/scripts/utils/BaseDeployIO.sol b/packages/contracts-bedrock/scripts/deploy/BaseDeployIO.sol similarity index 100% rename from packages/contracts-bedrock/scripts/utils/BaseDeployIO.sol rename to packages/contracts-bedrock/scripts/deploy/BaseDeployIO.sol diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index e68938392ee3..2931ebba1a38 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -16,13 +16,13 @@ import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol"; import { Process } from "scripts/libraries/Process.sol"; import { ChainAssertions } from "scripts/deploy/ChainAssertions.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; -import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/DeploySuperchain.s.sol"; +import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/deploy/DeploySuperchain.s.sol"; import { DeployImplementationsInput, DeployImplementations, DeployImplementationsInterop, DeployImplementationsOutput -} from "scripts/DeployImplementations.s.sol"; +} from "scripts/deploy/DeployImplementations.s.sol"; // Contracts import { StorageSetter } from "src/universal/StorageSetter.sol"; diff --git a/packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployAuthSystem.s.sol similarity index 100% rename from packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol rename to packages/contracts-bedrock/scripts/deploy/DeployAuthSystem.s.sol diff --git a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol similarity index 99% rename from packages/contracts-bedrock/scripts/DeployImplementations.s.sol rename to packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index af5f75e6125c..c9048a07dfa3 100644 --- a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -39,7 +39,7 @@ import { Blueprint } from "src/libraries/Blueprint.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; -import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol"; +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; // See DeploySuperchain.s.sol for detailed comments on the script architecture used here. contract DeployImplementationsInput is BaseDeployIO { diff --git a/packages/contracts-bedrock/scripts/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol similarity index 99% rename from packages/contracts-bedrock/scripts/DeployOPChain.s.sol rename to packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index a48d46348f7e..bc83402045d0 100644 --- a/packages/contracts-bedrock/scripts/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -7,7 +7,7 @@ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; -import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol"; +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; diff --git a/packages/contracts-bedrock/scripts/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol similarity index 99% rename from packages/contracts-bedrock/scripts/DeploySuperchain.s.sol rename to packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index 38c87f623443..74492556e1b1 100644 --- a/packages/contracts-bedrock/scripts/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -11,7 +11,7 @@ import { IProxy } from "src/universal/interfaces/IProxy.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; -import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol"; +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; // This comment block defines the requirements and rationale for the architecture used in this forge // script, along with other scripts that are being written as new Superchain-first deploy scripts to diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index fb008d4aa8d0..5b2260fce992 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; import { Test, stdStorage, StdStorage } from "forge-std/Test.sol"; -import { DeployOPChainInput } from "scripts/DeployOPChain.s.sol"; +import { DeployOPChainInput } from "scripts/deploy/DeployOPChain.s.sol"; import { DeployOPChain_TestBase } from "test/opcm/DeployOPChain.t.sol"; import { OPContractsManager } from "src/L1/OPContractsManager.sol"; diff --git a/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol b/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol index 19985ef490b9..58912409a09c 100644 --- a/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol @@ -5,7 +5,7 @@ import { Test, stdStorage, StdStorage } from "forge-std/Test.sol"; import { stdToml } from "forge-std/StdToml.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; -import { DeployAuthSystemInput, DeployAuthSystem, DeployAuthSystemOutput } from "scripts/DeployAuthSystem.s.sol"; +import { DeployAuthSystemInput, DeployAuthSystem, DeployAuthSystemOutput } from "scripts/deploy/DeployAuthSystem.s.sol"; contract DeployAuthSystemInput_Test is Test { DeployAuthSystemInput dasi; diff --git a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol index 490d764698ca..d01ef6a387b2 100644 --- a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol @@ -26,7 +26,7 @@ import { DeployImplementations, DeployImplementationsInterop, DeployImplementationsOutput -} from "scripts/DeployImplementations.s.sol"; +} from "scripts/deploy/DeployImplementations.s.sol"; contract DeployImplementationsInput_Test is Test { DeployImplementationsInput dii; diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index b218010fa37e..edfe4574bc7c 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/DeploySuperchain.s.sol"; +import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/deploy/DeploySuperchain.s.sol"; import { DeployImplementationsInput, DeployImplementations, DeployImplementationsInterop, DeployImplementationsOutput -} from "scripts/DeployImplementations.s.sol"; -import { DeployOPChainInput, DeployOPChain, DeployOPChainOutput } from "scripts/DeployOPChain.s.sol"; +} from "scripts/deploy/DeployImplementations.s.sol"; +import { DeployOPChainInput, DeployOPChain, DeployOPChainOutput } from "scripts/deploy/DeployOPChain.s.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; diff --git a/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol b/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol index 8641772a74d9..15e24f470f1c 100644 --- a/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol @@ -8,7 +8,7 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { Proxy } from "src/universal/Proxy.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { IProtocolVersions, ProtocolVersion } from "src/L1/interfaces/IProtocolVersions.sol"; -import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/DeploySuperchain.s.sol"; +import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/deploy/DeploySuperchain.s.sol"; contract DeploySuperchainInput_Test is Test { DeploySuperchainInput dsi; From 03bb1bccc03d35ab5d879381ba93293ed21a00bb Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Mon, 28 Oct 2024 15:12:52 +0100 Subject: [PATCH 055/451] rename LibStateDiff to StateDiff (#12696) --- packages/contracts-bedrock/scripts/deploy/Deploy.s.sol | 4 ++-- .../scripts/libraries/{LibStateDiff.sol => StateDiff.sol} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename packages/contracts-bedrock/scripts/libraries/{LibStateDiff.sol => StateDiff.sol} (99%) diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 2931ebba1a38..d40c03987d53 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -12,7 +12,7 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { Deployer } from "scripts/deploy/Deployer.sol"; import { Chains } from "scripts/libraries/Chains.sol"; import { Config } from "scripts/libraries/Config.sol"; -import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol"; +import { StateDiff } from "scripts/libraries/StateDiff.sol"; import { Process } from "scripts/libraries/Process.sol"; import { ChainAssertions } from "scripts/deploy/ChainAssertions.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; @@ -128,7 +128,7 @@ contract Deploy is Deployer { accesses.length, vm.toString(block.chainid) ); - string memory json = LibStateDiff.encodeAccountAccesses(accesses); + string memory json = StateDiff.encodeAccountAccesses(accesses); string memory statediffPath = string.concat(vm.projectRoot(), "/snapshots/state-diff/", vm.toString(block.chainid), ".json"); vm.writeJson({ json: json, path: statediffPath }); diff --git a/packages/contracts-bedrock/scripts/libraries/LibStateDiff.sol b/packages/contracts-bedrock/scripts/libraries/StateDiff.sol similarity index 99% rename from packages/contracts-bedrock/scripts/libraries/LibStateDiff.sol rename to packages/contracts-bedrock/scripts/libraries/StateDiff.sol index 2b6f38a9afa7..4b4cacab42f2 100644 --- a/packages/contracts-bedrock/scripts/libraries/LibStateDiff.sol +++ b/packages/contracts-bedrock/scripts/libraries/StateDiff.sol @@ -4,10 +4,10 @@ pragma solidity 0.8.15; import { stdJson } from "forge-std/StdJson.sol"; import { VmSafe } from "forge-std/Vm.sol"; -/// @title LibStateDiff +/// @title StateDiff /// @author refcell /// @notice Library to write StateDiff output to json. -library LibStateDiff { +library StateDiff { /// @notice Accepts an array of AccountAccess structs from the Vm and encodes them as a json string. /// @param _accountAccesses Array of AccountAccess structs. /// @return serialized_ string From 5f1851c15cb87fc467986ac6fdd8f323f96e88c0 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 28 Oct 2024 09:56:53 -0600 Subject: [PATCH 056/451] chore: Remove obsolete build files (#12700) * chore: Remove obsolete cloudbuild file * Remove coderabbit * remove snyk --- .coderabbit.yml | 24 ------------------------ .snyk | 3 --- cloudbuild.yaml | 12 ------------ 3 files changed, 39 deletions(-) delete mode 100644 .coderabbit.yml delete mode 100644 .snyk delete mode 100644 cloudbuild.yaml diff --git a/.coderabbit.yml b/.coderabbit.yml deleted file mode 100644 index f54403cf1571..000000000000 --- a/.coderabbit.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: "en" -early_access: false -reviews: - high_level_summary: false - poem: false - review_status: false - collapse_walkthrough: true - path_filters: - - "!**/*.json" - path_instructions: - - path: "**.sol" - instructions: "Focus on the following areas: - - Security - - Identifying test cases which are lacking - - Removing unnecessary code - " - auto_review: - enabled: false - drafts: false - base_branches: - - "develop" - - "feat/*" -chat: - auto_reply: true diff --git a/.snyk b/.snyk deleted file mode 100644 index 1558a7a82432..000000000000 --- a/.snyk +++ /dev/null @@ -1,3 +0,0 @@ -exclude: - global: - - infra/op-replica/** # snyk does not respect kustomizations, so not useful here diff --git a/cloudbuild.yaml b/cloudbuild.yaml deleted file mode 100644 index 2cf6dfb333bb..000000000000 --- a/cloudbuild.yaml +++ /dev/null @@ -1,12 +0,0 @@ -steps: - - name: 'gcr.io/kaniko-project/executor:latest' - args: - - --destination=us-central1-docker.pkg.dev/$PROJECT_ID/images/deployer-bedrock:$_TAG - - --destination=us-central1-docker.pkg.dev/$PROJECT_ID/images/deployer-bedrock:$COMMIT_SHA - - --dockerfile=./ops/docker/Dockerfile.packages - - --target=deployer-bedrock - - --cache=true - - --cache-ttl=48h - waitFor: ['-'] -options: - machineType: N1_HIGHCPU_32 From 0cb0cd6f44ed4fb5b8cc312c8e4981b03788bb42 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 28 Oct 2024 09:58:00 -0600 Subject: [PATCH 057/451] ci: Remove hosted semgrep (#12701) We're not getting a lot of value from hosted Semgrep. This PR removes that in favor of just adding rules to the `semgrep/` directory so they can be run locally in CI. --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f0789a504ef6..4db56cb4d3d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1314,7 +1314,6 @@ workflows: - contracts-bedrock-validate-spacers: requires: - contracts-bedrock-build - - semgrep-scan - semgrep-scan: name: semgrep-scan-local scan_command: semgrep scan --timeout=100 --config=./semgrep --error . From 788b2708d2aaee1639c716b636e848377661649f Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 09:36:17 -0700 Subject: [PATCH 058/451] cannon: Detect input/output codec mismatch before trace execution (#12676) * cannon: Check --input and --output file formats in CLI * cannon: Allow only binary state outputs in run cli * golang lint --- cannon/cmd/run.go | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 19352bad7d59..1a85158854b3 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -29,16 +29,16 @@ import ( var ( RunInputFlag = &cli.PathFlag{ Name: "input", - Usage: "path of input JSON state. Stdin if left empty.", + Usage: "path of input binary state. Stdin if left empty.", TakesFile: true, - Value: "state.json", + Value: "state.bin.gz", Required: true, } RunOutputFlag = &cli.PathFlag{ Name: "output", - Usage: "path of output JSON state. Not written if empty, use - to write to Stdout.", + Usage: "path of output binary state. Not written if empty, use - to write to Stdout.", TakesFile: true, - Value: "out.json", + Value: "out.bin.gz", Required: false, } patternHelp = "'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps" @@ -63,7 +63,7 @@ var ( RunSnapshotFmtFlag = &cli.StringFlag{ Name: "snapshot-fmt", Usage: "format for snapshot output file names.", - Value: "state-%d.json", + Value: "state-%d.bin.gz", Required: false, } RunStopAtFlag = &cli.GenericFlag{ @@ -279,6 +279,9 @@ func Run(ctx *cli.Context) error { if ctx.Bool(RunPProfCPU.Name) { defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop() } + if err := checkFlags(ctx); err != nil { + return err + } guestLogger := Logger(os.Stderr, log.LevelInfo) outLog := &mipsevm.LoggingWriter{Log: guestLogger.With("module", "guest", "stream", "stdout")} @@ -524,3 +527,17 @@ func CreateRunCommand(action cli.ActionFunc) *cli.Command { } var RunCommand = CreateRunCommand(Run) + +func checkFlags(ctx *cli.Context) error { + if output := ctx.Path(RunOutputFlag.Name); output != "" { + if !serialize.IsBinaryFile(output) { + return errors.New("invalid --output file format. Only binary file formats (ending in .bin or bin.gz) are supported") + } + } + if snapshotFmt := ctx.String(RunSnapshotFmtFlag.Name); snapshotFmt != "" { + if !serialize.IsBinaryFile(fmt.Sprintf(snapshotFmt, 0)) { + return errors.New("invalid --snapshot-fmt file format. Only binary file formats (ending in .bin or bin.gz) are supported") + } + } + return nil +} From 2cf297da0b87426df95d52e84a62ba7b48363a61 Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Mon, 28 Oct 2024 23:57:29 +0700 Subject: [PATCH 059/451] op-supervisor: Cross safe updates cycle detection + tests (#12530) * op-supervisor: experimental cross-safety, with hazard detection * tweak: Add some errors/error returns in backend/cross. * wip: Chain index <> ID mapping. * fix: Check parent instead of re-checking hazardBlock. * Remove Hazard Work * Write missing DB Bindings OpenBlock, LocallyDerivedFrom, CrossDerivedFrom * Configurable WorkFn for Workers * op-supervisor: move chain-index <> chain ID translation into dependency set, fix some interfaces * op-supervisor: update cross-safety worker routine * op-supervisor: update more error handling * op-supervisor: move errors to types package * op-supervisor: check CanExecuteAt and CanInitiateAt * op-supervisor: determine cross-safe candidate and L1 scope, and more fixes * todo L1 scope increment * op-supervisor: cross-safe L1 scope bump * op-supervisor: dependency set getter * op-supervisor: L1 scope increment fix * op-supervisor: fix cross-safe updates typing * op-node: signal L1 traversal of derivation to supervisor * op-supervisor: fromda fixes and tests * op-supervisor: fix OpenBlock, fix/add missing interface methods, hook up cross-safe worker routines * OpenBlock to return map[uint32]ExecutingMessage * Add Frontier Unit Tests * fix WithParent panic * op-node: register L1 traversal with op-supervisor * op-node,op-supervisor: add logging, temp work around for interop local-safe updates * Add safe_start_test, unsafe_start_test * Add safe_update_test and unsafe_update_test * add worker_test * op-supervisor: fix cross-safe L1 scope bumping * op-supervisor: fix logs DB test * Add cycle.go and initial unit tests. * fix: Use new execMsg log index info to complete graph. * debug: Add helper to write out Mermaid of our graph. * tests: Add more cycle tests. * tests: Organize cycle tests. * tests: Add NoCycle test. * fix: Add edges between basic logs. * tests: More comprehensive NoCycle tests. * tests: Make tests not use index-0 exec msgs for now. * fix: Disallow self-referencing messages. * fix: Safely handle unknown chains. * tests,fix: Remove unintended self-reference. * tests,fix: Remove unintended self-reference. * tests: Add test for the first log being exec. * refactor: Create ErrFailedToOpenBlock for error. * tests,refactor: Use table-driven tests to make it easier to iterate test vectors. * cleanup: Change some comments and make Mermaid debugging easier to disable. * tests: Add permuations of 2-cycles involving adjacency and first logs. * fix: When adding edges for execMsgs, remove node from inDegree0. * refactor: Split graph build from checks; abstract adding edges. This allows us to more easily test specifics of our cycle checking. Errors can be in either part, and only being able to test the entirety makes things a little difficult. Abstracting edge creation helps eliminate bugs like 118aeb6b8. * tests: Test execMsg to base log which is right after an execMsg to the first execMsg. * tests: Cycle across timestamps should not return an error. * fix: Don't consider cycles that depend on out-of-timestamp nodes. We do this by first collecting every execMsg from all chains, filtering out the out-of-timestamp nodes, and putting into a lookup map. When creating edges, we check to see if the target node is uninteresting. * fix: Remove debug mermaid logging. * cleanup: Remove stray text in comment. * tweak: Make presence check more idiomatic; remove possible nil map access. * docs: Improve comments in cycle detection code. * cleanup: Reorganize cycle detection code. * cleanup: Move public types to top. * cleanup: Change some names for consistency. * cleanup: Better comment in cycle detection. * docs: Explain graph representation. * tests: Re-organize HazardCycleChecks tests. * docs: Better naming and docs in cycle.go. * tests: Add large graph cycle detection tests. * tests: Add nil hazard map test. * refactor: Slightly tweak graph sort code to nest less; add comments. * tweak: Make self referencial messages fail with an ErrConflict. * tests: Add tests for both older/younger timestamps, and document that younger init msgs shouldn't occur. * tests: Add tests for 2-cycle with 3 chains. * tweak: Check that an exec msg's init msg exists. * tests: tweak test to use index 0 instead of 1. * cleanup: Remove unused function. * op-e2e: fix interop mock backend action test CI try 2 --------- Co-authored-by: protolambda Co-authored-by: axelKingsley --- .../supervisor/backend/cross/cycle.go | 302 ++++++++++ .../supervisor/backend/cross/cycle_test.go | 556 ++++++++++++++++++ 2 files changed, 858 insertions(+) create mode 100644 op-supervisor/supervisor/backend/cross/cycle.go create mode 100644 op-supervisor/supervisor/backend/cross/cycle_test.go diff --git a/op-supervisor/supervisor/backend/cross/cycle.go b/op-supervisor/supervisor/backend/cross/cycle.go new file mode 100644 index 000000000000..a872bc75a860 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/cycle.go @@ -0,0 +1,302 @@ +package cross + +import ( + "errors" + "fmt" + "strings" + + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +var ( + ErrCycle = errors.New("cycle detected") + ErrExecMsgHasInvalidIndex = errors.New("executing message has invalid log index") + ErrExecMsgUnknownChain = errors.New("executing message references unknown chain") +) + +// CycleCheckDeps is an interface for checking cyclical dependencies between logs. +type CycleCheckDeps interface { + // OpenBlock returns log data for the requested block, to be used for cycle checking. + OpenBlock(chainID types.ChainID, blockNum uint64) (seal types.BlockSeal, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) +} + +// node represents a log entry in the dependency graph. +// It could be an initiating message, executing message, both, or neither. +// It is uniquely identified by chain index and the log index within its parent block. +type node struct { + chainIndex types.ChainIndex + logIndex uint32 +} + +// graph is a directed graph of message dependencies. +// It is represented as an adjacency list with in-degree counts to be friendly to cycle checking. +type graph struct { + inDegree0 map[node]struct{} + inDegreeNon0 map[node]uint32 + outgoingEdges map[node][]node +} + +// addEdge adds a directed edge from -> to in the graph. +func (g *graph) addEdge(from, to node) { + // Remove the target from inDegree0 if it's there + delete(g.inDegree0, to) + + // Add or increment the target's in-degree count + g.inDegreeNon0[to] += 1 + + // Add the outgoing edge + g.outgoingEdges[from] = append(g.outgoingEdges[from], to) +} + +// HazardCycleChecks checks for cyclical dependencies between logs at the given timestamp. +// Here the timestamp invariant alone does not ensure ordering of messages. +// +// We perform this check in 3 steps: +// - Gather all logs across all hazard blocks at the given timestamp. +// - Build the logs into a directed graph of dependencies between logs. +// - Check the graph for cycles. +// +// The edges of the graph are determined by: +// - For all logs except the first in a block, there is an edge from the previous log. +// - For all executing messages, there is an edge from the initiating message. +// +// The edges between sequential logs ensure the graph is well-connected and free of any +// disjoint subgraphs that would make cycle checking more difficult. +// +// The cycle check is performed by executing Kahn's topological sort algorithm which +// succeeds if and only if a graph is acyclic. +// +// Returns nil if no cycles are found or ErrCycle if a cycle is detected. +func HazardCycleChecks(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) error { + g, err := buildGraph(d, inTimestamp, hazards) + if err != nil { + return err + } + + return checkGraph(g) +} + +// gatherLogs collects all log counts and executing messages across all hazard blocks. +// Returns: +// - map of chain index to its log count +// - map of chain index to map of log index to executing message (nil if doesn't exist or ignored) +func gatherLogs(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) ( + map[types.ChainIndex]uint32, + map[types.ChainIndex]map[uint32]*types.ExecutingMessage, + error, +) { + logCounts := make(map[types.ChainIndex]uint32) + execMsgs := make(map[types.ChainIndex]map[uint32]*types.ExecutingMessage) + + for hazardChainIndex, hazardBlock := range hazards { + // TODO(#11105): translate chain index to chain ID + hazardChainID := types.ChainIDFromUInt64(uint64(hazardChainIndex)) + bl, logCount, msgs, err := d.OpenBlock(hazardChainID, hazardBlock.Number) + if err != nil { + return nil, nil, fmt.Errorf("failed to open block: %w", err) + } + if bl != hazardBlock { + return nil, nil, fmt.Errorf("tried to open block %s of chain %s, but got different block %s than expected, use a reorg lock for consistency", hazardBlock, hazardChainID, bl) + } + + // Validate executing message indices + for logIdx := range msgs { + if logIdx >= logCount { + return nil, nil, fmt.Errorf("%w: log index %d >= log count %d", ErrExecMsgHasInvalidIndex, logIdx, logCount) + } + } + + // Store log count and in-timestamp executing messages + logCounts[hazardChainIndex] = logCount + + if len(msgs) > 0 { + if _, exists := execMsgs[hazardChainIndex]; !exists { + execMsgs[hazardChainIndex] = make(map[uint32]*types.ExecutingMessage) + } + } + for logIdx, msg := range msgs { + if msg.Timestamp == inTimestamp { + execMsgs[hazardChainIndex][logIdx] = msg + } + } + } + + return logCounts, execMsgs, nil +} + +// buildGraph constructs a dependency graph from the hazard blocks. +func buildGraph(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) (*graph, error) { + g := &graph{ + inDegree0: make(map[node]struct{}), + inDegreeNon0: make(map[node]uint32), + outgoingEdges: make(map[node][]node), + } + + logCounts, execMsgs, err := gatherLogs(d, inTimestamp, hazards) + if err != nil { + return nil, err + } + + // Add nodes for each log in the block, and add edges between sequential logs + for hazardChainIndex, logCount := range logCounts { + for i := uint32(0); i < logCount; i++ { + k := node{ + chainIndex: hazardChainIndex, + logIndex: i, + } + + if i == 0 { + // First log in block has no dependencies + g.inDegree0[k] = struct{}{} + } else { + // Add edge: prev log <> current log + prevKey := node{ + chainIndex: hazardChainIndex, + logIndex: i - 1, + } + g.addEdge(prevKey, k) + } + } + } + + // Add edges for executing messages to their initiating messages + for hazardChainIndex, msgs := range execMsgs { + for execLogIdx, m := range msgs { + // Error if the chain is unknown + if _, ok := hazards[m.Chain]; !ok { + return nil, ErrExecMsgUnknownChain + } + + // Check if we care about the init message + initChainMsgs, ok := execMsgs[m.Chain] + if !ok { + continue + } + if _, ok := initChainMsgs[m.LogIdx]; !ok { + continue + } + + // Check if the init message exists + if logCount, ok := logCounts[m.Chain]; !ok || m.LogIdx >= logCount { + return nil, fmt.Errorf("%w: initiating message log index out of bounds", types.ErrConflict) + } + + initKey := node{ + chainIndex: m.Chain, + logIndex: m.LogIdx, + } + execKey := node{ + chainIndex: hazardChainIndex, + logIndex: execLogIdx, + } + + // Disallow self-referencing messages + // This should not be possible since the executing message contains the hash of the initiating message. + if initKey == execKey { + return nil, fmt.Errorf("%w: self referencial message", types.ErrConflict) + } + + // Add the edge + g.addEdge(initKey, execKey) + } + } + + return g, nil +} + +// checkGraph uses Kahn's topological sort algorithm to check for cycles in the graph. +// +// Returns: +// - nil for acyclic graphs. +// - ErrCycle for cyclic graphs. +// +// Algorithm: +// 1. for each node with in-degree 0 (i.e. no dependencies), add it to the result, remove it from the work. +// 2. along with removing, remove the outgoing edges +// 3. if there is no node left with in-degree 0, then there is a cycle +func checkGraph(g *graph) error { + for { + // Process all nodes that have no incoming edges + for k := range g.inDegree0 { + // Remove all outgoing edges from this node + for _, out := range g.outgoingEdges[k] { + g.inDegreeNon0[out] -= 1 + if g.inDegreeNon0[out] == 0 { + delete(g.inDegreeNon0, out) + g.inDegree0[out] = struct{}{} + } + } + delete(g.outgoingEdges, k) + delete(g.inDegree0, k) + } + + // If there are new nodes with in-degree 0 then process them + if len(g.inDegree0) > 0 { + continue + } + + // We're done processing so check for remaining nodes + if len(g.inDegreeNon0) == 0 { + // Done, without cycles! + return nil + } + + // Some nodes left; there must be a cycle. + return ErrCycle + } +} + +// GenerateMermaidDiagram creates a Mermaid flowchart diagram from the graph data for debugging. +func GenerateMermaidDiagram(g *graph) string { + var sb strings.Builder + + sb.WriteString("flowchart TD\n") + + // Helper function to get a unique ID for each node + getNodeID := func(k node) string { + return fmt.Sprintf("N%d_%d", k.chainIndex, k.logIndex) + } + + // Helper function to get a label for each node + getNodeLabel := func(k node) string { + return fmt.Sprintf("C%d:L%d", k.chainIndex, k.logIndex) + } + + // Function to add a node to the diagram + addNode := func(k node, inDegree uint32) { + nodeID := getNodeID(k) + nodeLabel := getNodeLabel(k) + var shape string + if inDegree == 0 { + shape = "((%s))" + } else { + shape = "[%s]" + } + sb.WriteString(fmt.Sprintf(" %s"+shape+"\n", nodeID, nodeLabel)) + } + + // Add all nodes + for k := range g.inDegree0 { + addNode(k, 0) + } + for k, inDegree := range g.inDegreeNon0 { + addNode(k, inDegree) + } + + // Add all edges + for from, tos := range g.outgoingEdges { + fromID := getNodeID(from) + for _, to := range tos { + toID := getNodeID(to) + sb.WriteString(fmt.Sprintf(" %s --> %s\n", fromID, toID)) + } + } + + // Add a legend + sb.WriteString(" subgraph Legend\n") + sb.WriteString(" L1((In-Degree 0))\n") + sb.WriteString(" L2[In-Degree > 0]\n") + sb.WriteString(" end\n") + + return sb.String() +} diff --git a/op-supervisor/supervisor/backend/cross/cycle_test.go b/op-supervisor/supervisor/backend/cross/cycle_test.go new file mode 100644 index 000000000000..a90258b88a26 --- /dev/null +++ b/op-supervisor/supervisor/backend/cross/cycle_test.go @@ -0,0 +1,556 @@ +package cross + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type mockCycleCheckDeps struct { + openBlockFn func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) +} + +func (m *mockCycleCheckDeps) OpenBlock(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { + return m.openBlockFn(chainID, blockNum) +} + +type chainBlockDef struct { + logCount uint32 + messages map[uint32]*types.ExecutingMessage + error error +} + +type hazardCycleChecksTestCase struct { + name string + chainBlocks map[string]chainBlockDef + expectErr error + msg string + + // Optional overrides + hazards map[types.ChainIndex]types.BlockSeal + openBlockFn func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) +} + +func runHazardCycleChecksTestCaseGroup(t *testing.T, group string, tests []hazardCycleChecksTestCase) { + for _, tc := range tests { + t.Run(group+"/"+tc.name, func(t *testing.T) { + runHazardCycleChecksTestCase(t, tc) + }) + } +} + +func runHazardCycleChecksTestCase(t *testing.T, tc hazardCycleChecksTestCase) { + // Create mocked dependencies + deps := &mockCycleCheckDeps{ + openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { + // Use override if provided + if tc.openBlockFn != nil { + return tc.openBlockFn(chainID, blockNum) + } + + // Default behavior + chainStr := chainID.String() + def, ok := tc.chainBlocks[chainStr] + if !ok { + return types.BlockSeal{}, 0, nil, errors.New("unexpected chain") + } + if def.error != nil { + return types.BlockSeal{}, 0, nil, def.error + } + return types.BlockSeal{Number: blockNum}, def.logCount, def.messages, nil + }, + } + + // Generate hazards map automatically if not explicitly provided + var hazards map[types.ChainIndex]types.BlockSeal + if tc.hazards != nil { + hazards = tc.hazards + } else { + hazards = make(map[types.ChainIndex]types.BlockSeal) + for chainStr := range tc.chainBlocks { + hazards[chainIndex(chainStr)] = types.BlockSeal{Number: 1} + } + } + + // Run the test + err := HazardCycleChecks(deps, 100, hazards) + + // No error expected + if tc.expectErr == nil { + require.NoError(t, err, tc.msg) + return + } + + // Error expected, make sure it's the right one + require.Error(t, err, tc.msg) + if errors.Is(err, tc.expectErr) { + require.ErrorIs(t, err, tc.expectErr, tc.msg) + } else { + require.Contains(t, err.Error(), tc.expectErr.Error(), tc.msg) + } +} + +func chainIndex(s string) types.ChainIndex { + id, err := strconv.ParseUint(s, 10, 32) + if err != nil { + panic(fmt.Sprintf("invalid chain index in test: %v", err)) + } + return types.ChainIndex(id) +} + +func execMsg(chain string, logIdx uint32) *types.ExecutingMessage { + return execMsgWithTimestamp(chain, logIdx, 100) +} + +func execMsgWithTimestamp(chain string, logIdx uint32, timestamp uint64) *types.ExecutingMessage { + return &types.ExecutingMessage{ + Chain: chainIndex(chain), + LogIdx: logIdx, + Timestamp: timestamp, + } +} + +var emptyChainBlocks = map[string]chainBlockDef{ + "1": { + logCount: 0, + messages: map[uint32]*types.ExecutingMessage{}, + }, +} + +func TestHazardCycleChecksFailures(t *testing.T) { + testOpenBlockErr := errors.New("test OpenBlock error") + tests := []hazardCycleChecksTestCase{ + { + name: "empty hazards", + chainBlocks: emptyChainBlocks, + hazards: make(map[types.ChainIndex]types.BlockSeal), + expectErr: nil, + msg: "expected no error when there are no hazards", + }, + { + name: "nil hazards", + chainBlocks: emptyChainBlocks, + hazards: nil, + expectErr: nil, + msg: "expected no error when there are nil hazards", + }, + { + name: "nil blocks", + chainBlocks: nil, + hazards: nil, + expectErr: nil, + msg: "expected no error when there are nil blocks and hazards", + }, + { + name: "failed to open block error", + chainBlocks: emptyChainBlocks, + openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { + return types.BlockSeal{}, 0, nil, testOpenBlockErr + }, + expectErr: errors.New("failed to open block"), + msg: "expected error when OpenBlock fails", + }, + { + name: "block mismatch error", + chainBlocks: emptyChainBlocks, + // openBlockFn returns a block number that doesn't match the expected block number. + openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { + return types.BlockSeal{Number: blockNum + 1}, 0, make(map[uint32]*types.ExecutingMessage), nil + }, + expectErr: errors.New("tried to open block"), + msg: "expected error due to block mismatch", + }, + { + name: "invalid log index error", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 3, + messages: map[uint32]*types.ExecutingMessage{ + 5: execMsg("1", 0), // Invalid index >= logCount. + }, + }, + }, + expectErr: ErrExecMsgHasInvalidIndex, + msg: "expected invalid log index error", + }, + { + name: "self reference detected error", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 1, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 0), // Points at itself. + }, + }, + }, + expectErr: types.ErrConflict, + msg: "expected self reference detection error", + }, + { + name: "unknown chain", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("2", 0), // References chain 2 which isn't in hazards. + }, + }, + }, + hazards: map[types.ChainIndex]types.BlockSeal{ + 1: {Number: 1}, // Only include chain 1. + }, + expectErr: ErrExecMsgUnknownChain, + msg: "expected unknown chain error", + }, + } + runHazardCycleChecksTestCaseGroup(t, "Failure", tests) +} + +func TestHazardCycleChecksNoCycle(t *testing.T) { + tests := []hazardCycleChecksTestCase{ + { + name: "no logs", + chainBlocks: emptyChainBlocks, + expectErr: nil, + msg: "expected no cycle found for block with no logs", + }, + { + name: "one basic log", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 1, + messages: map[uint32]*types.ExecutingMessage{}, + }, + }, + msg: "expected no cycle found for single basic log", + }, + { + name: "one exec log", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 0), + }, + }, + }, + msg: "expected no cycle found for single exec log", + }, + { + name: "two basic logs", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{}, + }, + }, + msg: "expected no cycle found for two basic logs", + }, + { + name: "two exec logs to same target", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 3, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 0), + 2: execMsg("1", 0), + }, + }, + }, + msg: "expected no cycle found for two exec logs pointing at the same log", + }, + { + name: "two exec logs to different targets", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 3, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 0), + 2: execMsg("1", 1), + }, + }, + }, + msg: "expected no cycle found for two exec logs pointing at the different logs", + }, + { + name: "one basic log one exec log", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 0), + }, + }, + }, + msg: "expected no cycle found for one basic and one exec log", + }, + { + name: "first log is exec", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 1, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("2", 0), + }, + }, + "2": { + logCount: 1, + messages: nil, + }, + }, + msg: "expected no cycle found first log is exec", + }, + { + name: "cycle through older timestamp", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("2", 0), + 1: execMsgWithTimestamp("2", 1, 101), + }, + }, + "2": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 1), + }, + }, + }, + msg: "expected no cycle detection error for cycle through messages with different timestamps", + }, + // This should be caught by earlier validations, but included for completeness. + { + name: "cycle through younger timestamp", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("2", 0), + 1: execMsgWithTimestamp("2", 1, 99), + }, + }, + "2": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 1), + }, + }, + }, + msg: "expected no cycle detection error for cycle through messages with different timestamps", + }, + } + runHazardCycleChecksTestCaseGroup(t, "NoCycle", tests) +} + +func TestHazardCycleChecksCycle(t *testing.T) { + tests := []hazardCycleChecksTestCase{ + { + name: "2-cycle in single chain with first log", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 3, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 2), + 2: execMsg("1", 0), + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error", + }, + { + name: "2-cycle in single chain with first log, adjacent", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 1), + 1: execMsg("1", 0), + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error", + }, + { + name: "2-cycle in single chain, not first, adjacent", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 3, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 2), + 2: execMsg("1", 1), + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error", + }, + { + name: "2-cycle in single chain, not first, not adjacent", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 4, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 3), + 3: execMsg("1", 1), + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error", + }, + { + name: "2-cycle across chains", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("2", 0), + }, + }, + "2": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 0: execMsg("1", 1), + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error for cycle through executing messages", + }, + { + name: "3-cycle in single chain", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 4, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 2), // Points to log 2 + 2: execMsg("1", 3), // Points to log 3 + 3: execMsg("1", 1), // Points back to log 1 + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error for 3-node cycle", + }, + { + name: "cycle through adjacency dependency", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 10, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 5), // Points to log 5 + 5: execMsg("1", 2), // Points back to log 2 which is adjacent to log 1 + }, + }, + }, + expectErr: ErrCycle, + msg: "expected cycle detection error for when cycle goes through adjacency dependency", + }, + { + name: "2-cycle across chains with 3 hazard chains", + chainBlocks: map[string]chainBlockDef{ + "1": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("2", 1), + }, + }, + "2": { + logCount: 2, + messages: map[uint32]*types.ExecutingMessage{ + 1: execMsg("1", 1), + }, + }, + "3": {}, + }, + expectErr: ErrCycle, + hazards: map[types.ChainIndex]types.BlockSeal{ + 1: {Number: 1}, + 2: {Number: 1}, + 3: {Number: 1}, + }, + msg: "expected cycle detection error for cycle through executing messages", + }, + } + runHazardCycleChecksTestCaseGroup(t, "Cycle", tests) +} + +const ( + largeGraphChains = 10 + largeGraphLogsPerChain = 10000 +) + +func TestHazardCycleChecksLargeGraphNoCycle(t *testing.T) { + // Create a large but acyclic graph + chainBlocks := make(map[string]chainBlockDef) + for i := 1; i <= largeGraphChains; i++ { + msgs := make(map[uint32]*types.ExecutingMessage) + // Create a chain of dependencies across chains + if i > 1 { + for j := uint32(0); j < largeGraphLogsPerChain; j++ { + // Point to previous chain, same log index + msgs[j] = execMsg(strconv.Itoa(i-1), j) + } + } + chainBlocks[strconv.Itoa(i)] = chainBlockDef{ + logCount: largeGraphLogsPerChain, + messages: msgs, + } + } + + tc := hazardCycleChecksTestCase{ + name: "Large graph without cycles", + chainBlocks: chainBlocks, + expectErr: nil, + msg: "expected no cycle in large acyclic graph", + } + runHazardCycleChecksTestCase(t, tc) +} + +func TestHazardCycleChecksLargeGraphCycle(t *testing.T) { + // Create a large graph with a cycle hidden in it + const cycleChain = 3 + const cycleLogIndex = 5678 + + chainBlocks := make(map[string]chainBlockDef) + for i := 1; i <= largeGraphChains; i++ { + msgs := make(map[uint32]*types.ExecutingMessage) + + // Create a chain of dependencies across chains + if i > 1 { + for j := uint32(0); j < largeGraphLogsPerChain; j++ { + if i == cycleChain && j == cycleLogIndex { + // Create a cycle by pointing back to chain 1 + msgs[j] = execMsg("1", cycleLogIndex+1) + } else { + // Normal case: point to previous chain, same log index + msgs[j] = execMsg(strconv.Itoa(i-1), j) + } + } + } else { + // In chain 1, create the other side of the cycle + msgs[cycleLogIndex+1] = execMsg(strconv.Itoa(cycleChain), cycleLogIndex) + } + + chainBlocks[strconv.Itoa(i)] = chainBlockDef{ + logCount: largeGraphLogsPerChain, + messages: msgs, + } + } + + tc := hazardCycleChecksTestCase{ + name: "Large graph with cycle", + chainBlocks: chainBlocks, + expectErr: ErrCycle, + msg: "expected to detect cycle in large cyclic graph", + } + runHazardCycleChecksTestCase(t, tc) +} From 73094cb34624bc3fc72e568b5b66dd0c89d47542 Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 09:59:33 -0700 Subject: [PATCH 060/451] cannon: Implement bltzal (#12594) --- cannon/mipsevm/README.md | 1 + cannon/mipsevm/exec/mips_instructions.go | 5 +++++ cannon/mipsevm/tests/evm_common_test.go | 9 +++++++++ packages/contracts-bedrock/semver-lock.json | 8 ++++---- packages/contracts-bedrock/src/cannon/MIPS.sol | 4 ++-- packages/contracts-bedrock/src/cannon/MIPS2.sol | 4 ++-- .../src/cannon/libraries/MIPSInstructions.sol | 5 +++++ 7 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cannon/mipsevm/README.md b/cannon/mipsevm/README.md index 03afec250a4a..f01aa61510e5 100644 --- a/cannon/mipsevm/README.md +++ b/cannon/mipsevm/README.md @@ -15,6 +15,7 @@ Supported 63 instructions: | `Conditional Branch` | `bgezal` | Branch and link on greater than or equal to zero. | | `Conditional Branch` | `blez` | Branch on less than or equal to zero. | | `Conditional Branch` | `bltz` | Branch on less than zero. | +| `Conditional Branch` | `bltzal` | Branch and link on less than zero. | | `Conditional Branch` | `bne` | Branch on not equal. | | `Logical` | `clo` | Count leading ones. | | `Logical` | `clz` | Count leading zeros. | diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 9cc94e98e50c..39d40dd59dc0 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -486,6 +486,11 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i if rtv == 0 { // bltz shouldBranch = arch.SignedInteger(rs) < 0 } + if rtv == 0x10 { // bltzal + shouldBranch = arch.SignedInteger(rs) < 0 + registers[31] = cpu.PC + 8 // always set regardless of branch taken + linked = true + } if rtv == 1 { // bgez shouldBranch = arch.SignedInteger(rs) >= 0 } diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index 6f5063ec12bd..932662fd6979 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -1032,6 +1032,15 @@ func TestEVMSingleStepBranch(t *testing.T) { {name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, {name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + // bltzal t0, $x + {name: "bltzal", pc: 0, opcode: 0x1, regimm: 0x10, rs: 0x5, offset: 0x100, expectNextPC: 0x8, expectLink: true}, + {name: "bltzal large rs", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bltzal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: 0x0, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bltzal sign rs", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: -1, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bltzal rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bltzal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14, expectLink: true}, + {name: "bltzal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x10, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true}, + // bgez t0, $x {name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, {name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 57567ec570c7..4210b233a66b 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -140,12 +140,12 @@ "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, "src/cannon/MIPS.sol": { - "initCodeHash": "0x94f2e8f7818a990c009cccb501216a7f6df29b05d8f178cfff10a40288bf588e", - "sourceCodeHash": "0x786d4947488a771a426cc38de307ae99b2c2af1efca38b7655c60be7c019371f" + "initCodeHash": "0xa3cbf121bad13c00227ea4fef128853d9a86b7ec9158de894f99b58d38d7630a", + "sourceCodeHash": "0xd8467700c80b3e62fa37193dc6513bac35282094b686b50e162e157f704dde00" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0x3744036fa240c7d57f39307c0bf27cb7027d05d6b4f52142d5122b6e538ee0b2", - "sourceCodeHash": "0x5f79a0f99a288d570df243ea9560e67a319d1685b3209ae457fc714a76ff2908" + "initCodeHash": "0x478fdad3eccd158822ce2025971a9242c37c976024f419fba417fe54158269b7", + "sourceCodeHash": "0x81dc3329c1644afa30ecd2684f44f8b96b5a17612dcfa6476432eed697209e63" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 7e16d48a3dbb..5efab0ce98f1 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -44,8 +44,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.6 - string public constant version = "1.2.1-beta.6"; + /// @custom:semver 1.2.1-beta.7 + string public constant version = "1.2.1-beta.7"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index b01b5a78d3de..34851f61a27f 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.19 - string public constant version = "1.0.0-beta.19"; + /// @custom:semver 1.0.0-beta.20 + string public constant version = "1.0.0-beta.20"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol index 32ec9007fd7e..fa4d1451b54b 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol @@ -495,6 +495,11 @@ library MIPSInstructions { if (rtv == 0) { shouldBranch = int32(_rs) < 0; } + // bltzal + if (rtv == 0x10) { + shouldBranch = int32(_rs) < 0; + _registers[31] = _cpu.pc + 8; // always set regardless of branch taken + } if (rtv == 1) { shouldBranch = int32(_rs) >= 0; } From 882d261b49b427777d8cedab4a826e4f8388cd18 Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 29 Oct 2024 00:11:45 +0700 Subject: [PATCH 061/451] op-supervisor: make frontend/backend/client interfaces consistent (#12699) --- op-e2e/actions/interop/interop_test.go | 12 ++++---- op-node/rollup/interop/interop.go | 16 ++++++++--- op-node/rollup/interop/interop_test.go | 4 +-- op-service/eth/id.go | 9 ++++++ op-service/sources/supervisor_client.go | 8 +++--- op-service/testutils/fake_interop_backend.go | 8 +++--- op-service/testutils/mock_interop_backend.go | 8 +++--- op-supervisor/supervisor/backend/backend.go | 12 ++++---- .../supervisor/backend/backend_test.go | 2 +- op-supervisor/supervisor/backend/db/query.go | 14 ++++++++-- op-supervisor/supervisor/backend/mock.go | 10 +++---- op-supervisor/supervisor/frontend/frontend.go | 28 +++++++++++-------- 12 files changed, 81 insertions(+), 50 deletions(-) diff --git a/op-e2e/actions/interop/interop_test.go b/op-e2e/actions/interop/interop_test.go index b005baf5f0fa..a69a09a9bd94 100644 --- a/op-e2e/actions/interop/interop_test.go +++ b/op-e2e/actions/interop/interop_test.go @@ -41,12 +41,12 @@ func TestInteropVerifier(gt *testing.T) { helpers.WithInteropBackend(verMockBackend)) // Genesis block will be registered as local-safe when we first trigger the derivation pipeline - seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) return nil } - verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) return nil @@ -56,7 +56,7 @@ func TestInteropVerifier(gt *testing.T) { ver.ActL2PipelineFull(t) l2ChainID := types.ChainIDFromBig(sd.RollupCfg.L2ChainID) - seqMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { + seqMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { require.Equal(t, chainID, l2ChainID) require.Equal(t, uint64(1), head.Number) return nil @@ -105,7 +105,7 @@ func TestInteropVerifier(gt *testing.T) { // Sync the L1 block, to verify the L2 block as local-safe. seqMockBackend.UpdateLocalUnsafeFn = nil nextL2 := uint64(0) - seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { require.Equal(t, nextL2, lastDerived.Number) nextL2 += 1 return nil @@ -153,12 +153,12 @@ func TestInteropVerifier(gt *testing.T) { require.Equal(t, uint64(1), status.SafeL2.Number, "cross-safe reached") require.Equal(t, uint64(0), status.FinalizedL2.Number) - verMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { + verMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { require.Equal(t, uint64(1), head.Number) return nil } nextL2 = 0 - verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { + verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { require.Equal(t, nextL2, lastDerived.Number) nextL2 += 1 require.Equal(t, l1Head.ID(), derivedFrom.ID()) diff --git a/op-node/rollup/interop/interop.go b/op-node/rollup/interop/interop.go index 293402fee389..c929c8f0d01d 100644 --- a/op-node/rollup/interop/interop.go +++ b/op-node/rollup/interop/interop.go @@ -15,6 +15,8 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/event" "github.com/ethereum-optimism/optimism/op-node/rollup/finality" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -27,11 +29,17 @@ type InteropBackend interface { DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) - UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error - UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error + UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error + UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.L1BlockRef) error } +// For testing usage, the backend of the supervisor implements the interface, no need for RPC. +var _ InteropBackend = (*backend.SupervisorBackend)(nil) + +// For RPC usage, the supervisor client implements the interop backend. +var _ InteropBackend = (*sources.SupervisorClient)(nil) + type L2Source interface { L2BlockRefByNumber(context.Context, uint64) (eth.L2BlockRef, error) L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) @@ -117,7 +125,7 @@ func (d *InteropDeriver) onLocalUnsafeUpdate(x engine.UnsafeUpdateEvent) { d.log.Debug("Signaling unsafe L2 head update to interop backend", "head", x.Ref) ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout) defer cancel() - if err := d.backend.UpdateLocalUnsafe(ctx, d.chainID, x.Ref); err != nil { + if err := d.backend.UpdateLocalUnsafe(ctx, d.chainID, x.Ref.BlockRef()); err != nil { d.log.Warn("Failed to signal unsafe L2 head to interop backend", "head", x.Ref, "err", err) // still continue to try and do a cross-unsafe update } @@ -129,7 +137,7 @@ func (d *InteropDeriver) onInteropPendingSafeChangedEvent(x engine.InteropPendin d.log.Debug("Signaling derived-from update to interop backend", "derivedFrom", x.DerivedFrom, "block", x.Ref) ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout) defer cancel() - if err := d.backend.UpdateLocalSafe(ctx, d.chainID, x.DerivedFrom, x.Ref); err != nil { + if err := d.backend.UpdateLocalSafe(ctx, d.chainID, x.DerivedFrom, x.Ref.BlockRef()); err != nil { d.log.Debug("Failed to signal derived-from update to interop backend", "derivedFrom", x.DerivedFrom, "block", x.Ref) // still continue to try and do a cross-safe update } diff --git a/op-node/rollup/interop/interop_test.go b/op-node/rollup/interop/interop_test.go index fc6bf59cacea..d4fb87f0dd92 100644 --- a/op-node/rollup/interop/interop_test.go +++ b/op-node/rollup/interop/interop_test.go @@ -36,7 +36,7 @@ func TestInteropDeriver(t *testing.T) { t.Run("local-unsafe blocks push to supervisor and trigger cross-unsafe attempts", func(t *testing.T) { emitter.ExpectOnce(engine.RequestCrossUnsafeEvent{}) unsafeHead := testutils.RandomL2BlockRef(rng) - interopBackend.ExpectUpdateLocalUnsafe(chainID, unsafeHead, nil) + interopBackend.ExpectUpdateLocalUnsafe(chainID, unsafeHead.BlockRef(), nil) interopDeriver.OnEvent(engine.UnsafeUpdateEvent{Ref: unsafeHead}) emitter.AssertExpectations(t) interopBackend.AssertExpectations(t) @@ -93,7 +93,7 @@ func TestInteropDeriver(t *testing.T) { emitter.ExpectOnce(engine.RequestCrossSafeEvent{}) derivedFrom := testutils.RandomBlockRef(rng) localSafe := testutils.RandomL2BlockRef(rng) - interopBackend.ExpectUpdateLocalSafe(chainID, derivedFrom, localSafe, nil) + interopBackend.ExpectUpdateLocalSafe(chainID, derivedFrom, localSafe.BlockRef(), nil) interopDeriver.OnEvent(engine.InteropPendingSafeChangedEvent{ Ref: localSafe, DerivedFrom: derivedFrom, diff --git a/op-service/eth/id.go b/op-service/eth/id.go index c323d1e69b9a..3eecdbc3bac1 100644 --- a/op-service/eth/id.go +++ b/op-service/eth/id.go @@ -49,6 +49,15 @@ func (id L2BlockRef) TerminalString() string { return fmt.Sprintf("%s:%d", id.Hash.TerminalString(), id.Number) } +func (id L2BlockRef) BlockRef() BlockRef { + return BlockRef{ + Hash: id.Hash, + Number: id.Number, + ParentHash: id.ParentHash, + Time: id.Time, + } +} + type L1BlockRef struct { Hash common.Hash `json:"hash"` Number uint64 `json:"number"` diff --git a/op-service/sources/supervisor_client.go b/op-service/sources/supervisor_client.go index fc4c85b10a45..d85fc4901f31 100644 --- a/op-service/sources/supervisor_client.go +++ b/op-service/sources/supervisor_client.go @@ -97,17 +97,17 @@ func (cl *SupervisorClient) Finalized(ctx context.Context, chainID types.ChainID return result, err } -func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { - var result eth.L1BlockRef +func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.BlockRef, error) { + var result eth.BlockRef err := cl.client.CallContext(ctx, &result, "supervisor_derivedFrom", chainID, derived) return result, err } -func (cl *SupervisorClient) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { +func (cl *SupervisorClient) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { return cl.client.CallContext(ctx, nil, "supervisor_updateLocalUnsafe", chainID, head) } -func (cl *SupervisorClient) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { +func (cl *SupervisorClient) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { return cl.client.CallContext(ctx, nil, "supervisor_updateLocalSafe", chainID, derivedFrom, lastDerived) } diff --git a/op-service/testutils/fake_interop_backend.go b/op-service/testutils/fake_interop_backend.go index b73b4ac07225..4ef439a9a3bc 100644 --- a/op-service/testutils/fake_interop_backend.go +++ b/op-service/testutils/fake_interop_backend.go @@ -12,8 +12,8 @@ type FakeInteropBackend struct { SafeViewFn func(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) FinalizedFn func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) DerivedFromFn func(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) - UpdateLocalUnsafeFn func(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error - UpdateLocalSafeFn func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error + UpdateLocalUnsafeFn func(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error + UpdateLocalSafeFn func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error UpdateFinalizedL1Fn func(ctx context.Context, chainID types.ChainID, finalized eth.L1BlockRef) error } @@ -33,11 +33,11 @@ func (m *FakeInteropBackend) DerivedFrom(ctx context.Context, chainID types.Chai return m.DerivedFromFn(ctx, chainID, derived) } -func (m *FakeInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { +func (m *FakeInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { return m.UpdateLocalUnsafeFn(ctx, chainID, head) } -func (m *FakeInteropBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { +func (m *FakeInteropBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { return m.UpdateLocalSafeFn(ctx, chainID, derivedFrom, lastDerived) } diff --git a/op-service/testutils/mock_interop_backend.go b/op-service/testutils/mock_interop_backend.go index 087b2f9c28cf..6724acedd43f 100644 --- a/op-service/testutils/mock_interop_backend.go +++ b/op-service/testutils/mock_interop_backend.go @@ -67,12 +67,12 @@ func (m *MockInteropBackend) ExpectDerivedFrom(chainID types.ChainID, derived et m.Mock.On("DerivedFrom", chainID, derived).Once().Return(result, &err) } -func (m *MockInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.L2BlockRef) error { +func (m *MockInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { result := m.Mock.MethodCalled("UpdateLocalUnsafe", chainID, head) return *result.Get(0).(*error) } -func (m *MockInteropBackend) ExpectUpdateLocalUnsafe(chainID types.ChainID, head eth.L2BlockRef, err error) { +func (m *MockInteropBackend) ExpectUpdateLocalUnsafe(chainID types.ChainID, head eth.BlockRef, err error) { m.Mock.On("UpdateLocalUnsafe", chainID, head).Once().Return(&err) } @@ -80,12 +80,12 @@ func (m *MockInteropBackend) ExpectAnyUpdateLocalUnsafe(chainID types.ChainID, e m.Mock.On("UpdateLocalUnsafe", chainID, mock.Anything).Once().Return(&err) } -func (m *MockInteropBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef) error { +func (m *MockInteropBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { result := m.Mock.MethodCalled("UpdateLocalSafe", chainID, derivedFrom, lastDerived) return *result.Get(0).(*error) } -func (m *MockInteropBackend) ExpectUpdateLocalSafe(chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.L2BlockRef, err error) { +func (m *MockInteropBackend) ExpectUpdateLocalSafe(chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef, err error) { m.Mock.On("UpdateLocalSafe", chainID, derivedFrom, lastDerived).Once().Return(&err) } diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 898157558931..5768e16e3ca0 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -402,21 +402,21 @@ func (su *SupervisorBackend) Finalized(ctx context.Context, chainID types.ChainI return v.ID(), nil } -func (su *SupervisorBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockID, err error) { +func (su *SupervisorBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { su.mu.RLock() defer su.mu.RUnlock() v, err := su.chainDBs.DerivedFrom(chainID, derived) if err != nil { - return eth.BlockID{}, err + return eth.BlockRef{}, err } - return v.ID(), nil + return v, nil } // Update methods // ---------------------------- -func (su *SupervisorBackend) UpdateLocalUnsafe(chainID types.ChainID, head eth.BlockRef) error { +func (su *SupervisorBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { su.mu.RLock() defer su.mu.RUnlock() ch, ok := su.chainProcessors[chainID] @@ -426,14 +426,14 @@ func (su *SupervisorBackend) UpdateLocalUnsafe(chainID types.ChainID, head eth.B return ch.OnNewHead(head) } -func (su *SupervisorBackend) UpdateLocalSafe(chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { +func (su *SupervisorBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { su.mu.RLock() defer su.mu.RUnlock() return su.chainDBs.UpdateLocalSafe(chainID, derivedFrom, lastDerived) } -func (su *SupervisorBackend) UpdateFinalizedL1(chainID types.ChainID, finalized eth.BlockRef) error { +func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error { su.mu.RLock() defer su.mu.RUnlock() diff --git a/op-supervisor/supervisor/backend/backend_test.go b/op-supervisor/supervisor/backend/backend_test.go index 3d2b5ad123a5..6ddfcac662b2 100644 --- a/op-supervisor/supervisor/backend/backend_test.go +++ b/op-supervisor/supervisor/backend/backend_test.go @@ -113,7 +113,7 @@ func TestBackendLifetime(t *testing.T) { src.ExpectL1BlockRefByNumber(2, eth.L1BlockRef{}, ethereum.NotFound) - err = b.UpdateLocalUnsafe(chainA, blockY) + err = b.UpdateLocalUnsafe(context.Background(), chainA, blockY) require.NoError(t, err) // Make the processing happen, so we can rely on the new chain information, // and not run into errors for future data that isn't mocked at this time. diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index 5db821396c8a..7dc2e72d3e72 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -167,15 +167,23 @@ func (db *ChainsDB) LastDerivedFrom(chainID types.ChainID, derivedFrom eth.Block return crossDB.LastDerivedAt(derivedFrom) } -func (db *ChainsDB) DerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { +func (db *ChainsDB) DerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { db.mu.RLock() defer db.mu.RUnlock() localDB, ok := db.localDBs[chainID] if !ok { - return types.BlockSeal{}, types.ErrUnknownChain + return eth.BlockRef{}, types.ErrUnknownChain + } + res, err := localDB.DerivedFrom(derived) + if err != nil { + return eth.BlockRef{}, err + } + parent, err := localDB.PreviousDerivedFrom(res.ID()) + if err != nil { + return eth.BlockRef{}, err } - return localDB.DerivedFrom(derived) + return res.WithParent(parent.ID()), nil } // Check calls the underlying logDB to determine if the given log entry exists at the given location. diff --git a/op-supervisor/supervisor/backend/mock.go b/op-supervisor/supervisor/backend/mock.go index 0cee49463197..c07e74a40013 100644 --- a/op-supervisor/supervisor/backend/mock.go +++ b/op-supervisor/supervisor/backend/mock.go @@ -62,19 +62,19 @@ func (m *MockBackend) Finalized(ctx context.Context, chainID types.ChainID) (eth return eth.BlockID{}, nil } -func (m *MockBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockID, err error) { - return eth.BlockID{}, nil +func (m *MockBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { + return eth.BlockRef{}, nil } -func (m *MockBackend) UpdateLocalUnsafe(chainID types.ChainID, head eth.BlockRef) error { +func (m *MockBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { return nil } -func (m *MockBackend) UpdateLocalSafe(chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { +func (m *MockBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { return nil } -func (m *MockBackend) UpdateFinalizedL1(chainID types.ChainID, finalized eth.BlockRef) error { +func (m *MockBackend) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error { return nil } diff --git a/op-supervisor/supervisor/frontend/frontend.go b/op-supervisor/supervisor/frontend/frontend.go index 6b87b219e075..26e751200a5a 100644 --- a/op-supervisor/supervisor/frontend/frontend.go +++ b/op-supervisor/supervisor/frontend/frontend.go @@ -17,16 +17,16 @@ type AdminBackend interface { type QueryBackend interface { CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) CheckMessages(messages []types.Message, minSafety types.SafetyLevel) error - DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockID, err error) + DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) UnsafeView(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) SafeView(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) } type UpdatesBackend interface { - UpdateLocalUnsafe(chainID types.ChainID, head eth.BlockRef) error - UpdateLocalSafe(chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error - UpdateFinalizedL1(chainID types.ChainID, finalized eth.BlockRef) error + UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error + UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error + UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error } type Backend interface { @@ -39,6 +39,8 @@ type QueryFrontend struct { Supervisor QueryBackend } +var _ QueryBackend = (*QueryFrontend)(nil) + // CheckMessage checks the safety-level of an individual message. // The payloadHash references the hash of the message-payload of the message. func (q *QueryFrontend) CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) { @@ -65,7 +67,7 @@ func (q *QueryFrontend) Finalized(ctx context.Context, chainID types.ChainID) (e return q.Supervisor.Finalized(ctx, chainID) } -func (q *QueryFrontend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockID, err error) { +func (q *QueryFrontend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { return q.Supervisor.DerivedFrom(ctx, chainID, derived) } @@ -73,6 +75,8 @@ type AdminFrontend struct { Supervisor Backend } +var _ AdminBackend = (*AdminFrontend)(nil) + // Start starts the service, if it was previously stopped. func (a *AdminFrontend) Start(ctx context.Context) error { return a.Supervisor.Start(ctx) @@ -92,14 +96,16 @@ type UpdatesFrontend struct { Supervisor UpdatesBackend } -func (u *UpdatesFrontend) UpdateLocalUnsafe(chainID types.ChainID, head eth.BlockRef) error { - return u.Supervisor.UpdateLocalUnsafe(chainID, head) +var _ UpdatesBackend = (*UpdatesFrontend)(nil) + +func (u *UpdatesFrontend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { + return u.Supervisor.UpdateLocalUnsafe(ctx, chainID, head) } -func (u *UpdatesFrontend) UpdateLocalSafe(chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { - return u.Supervisor.UpdateLocalSafe(chainID, derivedFrom, lastDerived) +func (u *UpdatesFrontend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { + return u.Supervisor.UpdateLocalSafe(ctx, chainID, derivedFrom, lastDerived) } -func (u *UpdatesFrontend) UpdateFinalizedL1(chainID types.ChainID, finalized eth.BlockRef) error { - return u.Supervisor.UpdateFinalizedL1(chainID, finalized) +func (u *UpdatesFrontend) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error { + return u.Supervisor.UpdateFinalizedL1(ctx, chainID, finalized) } From 005116d9b4e39c7ba0586ce95ceba4067123efd5 Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 11:27:42 -0700 Subject: [PATCH 062/451] cannon: Noop SYS_STAT for the mt stf (#12687) --- cannon/mipsevm/arch/arch32.go | 1 + cannon/mipsevm/arch/arch64.go | 1 + cannon/mipsevm/multithreaded/mips.go | 1 + cannon/mipsevm/tests/evm_multithreaded_test.go | 1 + packages/contracts-bedrock/semver-lock.json | 4 ++-- packages/contracts-bedrock/src/cannon/MIPS2.sol | 6 ++++-- .../contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol | 1 + 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cannon/mipsevm/arch/arch32.go b/cannon/mipsevm/arch/arch32.go index dbe9ee193d7a..4f9329ef9ce3 100644 --- a/cannon/mipsevm/arch/arch32.go +++ b/cannon/mipsevm/arch/arch32.go @@ -61,6 +61,7 @@ const ( SysPrlimit64 = 4338 SysClose = 4006 SysPread64 = 4200 + SysStat = 4106 SysFstat = 4108 SysFstat64 = 4215 SysOpenAt = 4288 diff --git a/cannon/mipsevm/arch/arch64.go b/cannon/mipsevm/arch/arch64.go index b260ebb1bbb3..b6a23f5fde9e 100644 --- a/cannon/mipsevm/arch/arch64.go +++ b/cannon/mipsevm/arch/arch64.go @@ -69,6 +69,7 @@ const ( SysPrlimit64 = 5297 SysClose = 5003 SysPread64 = 5016 + SysStat = 5004 SysFstat = 5005 SysFstat64 = UndefinedSysNr SysOpenAt = 5247 diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index f371da2c4053..65c911d9c9eb 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -187,6 +187,7 @@ func (m *InstrumentedState) handleSyscall() error { case arch.SysPrlimit64: case arch.SysClose: case arch.SysPread64: + case arch.SysStat: case arch.SysFstat: case arch.SysOpenAt: case arch.SysReadlink: diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 3d873730306a..4b663f3b7f11 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -1103,6 +1103,7 @@ var NoopSyscalls = map[string]uint32{ "SysPrlimit64": 4338, "SysClose": 4006, "SysPread64": 4200, + "SysStat": 4106, "SysFstat": 4108, "SysFstat64": 4215, "SysOpenAt": 4288, diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 4210b233a66b..9f7240772437 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -144,8 +144,8 @@ "sourceCodeHash": "0xd8467700c80b3e62fa37193dc6513bac35282094b686b50e162e157f704dde00" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0x478fdad3eccd158822ce2025971a9242c37c976024f419fba417fe54158269b7", - "sourceCodeHash": "0x81dc3329c1644afa30ecd2684f44f8b96b5a17612dcfa6476432eed697209e63" + "initCodeHash": "0xaedf0d0b0e94a0c5e7d987331d2fdba84230f5704a6ca33677e70cde7051b17e", + "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 34851f61a27f..220c66ddde54 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.20 - string public constant version = "1.0.0-beta.20"; + /// @custom:semver 1.0.0-beta.21 + string public constant version = "1.0.0-beta.21"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -555,6 +555,8 @@ contract MIPS2 is ISemver { // ignored } else if (syscall_no == sys.SYS_PREAD64) { // ignored + } else if (syscall_no == sys.SYS_STAT) { + // ignored } else if (syscall_no == sys.SYS_FSTAT) { // ignored } else if (syscall_no == sys.SYS_OPENAT) { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol index 4354163a9d3f..ec0cfdded894 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol @@ -53,6 +53,7 @@ library MIPSSyscalls { uint32 internal constant SYS_PRLIMIT64 = 4338; uint32 internal constant SYS_CLOSE = 4006; uint32 internal constant SYS_PREAD64 = 4200; + uint32 internal constant SYS_STAT = 4106; uint32 internal constant SYS_FSTAT = 4108; uint32 internal constant SYS_FSTAT64 = 4215; uint32 internal constant SYS_OPENAT = 4288; From da681773eb109f19041ebdaaf24c264ec74e5cef Mon Sep 17 00:00:00 2001 From: mbaxter Date: Mon, 28 Oct 2024 15:30:05 -0400 Subject: [PATCH 063/451] cannon: Update program loading for 64-bit programs (#12657) * cannon: Add some unit tests for LoadELF * cannon: Fix off-by-one boundary check * cannon: Adapt LoadELF addr check for 64-bit * cannon: Handle zero-length segments * cannon: Restrict virtual address space to 48-bits for MIPS64 --- cannon/mipsevm/program/load.go | 27 +++-- cannon/mipsevm/program/load_test.go | 83 +++++++++++++++ cannon/mipsevm/program/testutil/mocks.go | 130 +++++++++++++++++++++++ 3 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 cannon/mipsevm/program/load_test.go create mode 100644 cannon/mipsevm/program/testutil/mocks.go diff --git a/cannon/mipsevm/program/load.go b/cannon/mipsevm/program/load.go index 3cbba07d2bcd..23a1c67b1547 100644 --- a/cannon/mipsevm/program/load.go +++ b/cannon/mipsevm/program/load.go @@ -25,7 +25,7 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[ s := initState(Word(f.Entry), HEAP_START) for i, prog := range f.Progs { - if prog.Type == 0x70000003 { // MIPS_ABIFLAGS + if prog.Type == elf.PT_MIPS_ABIFLAGS { continue } @@ -42,12 +42,27 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[ } } - // TODO(#12205) - if prog.Vaddr+prog.Memsz >= uint64(1<<32) { - return empty, fmt.Errorf("program %d out of 32-bit mem range: %x - %x (size: %x)", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) + if prog.Memsz == 0 { + // Nothing to do + continue + } + + // Calculate the architecture-specific last valid memory address + var lastMemoryAddr uint64 + if arch.IsMips32 { + // 32-bit virtual address space + lastMemoryAddr = (1 << 32) - 1 + } else { + // 48-bit virtual address space + lastMemoryAddr = (1 << 48) - 1 + } + + lastByteToWrite := prog.Vaddr + prog.Memsz - 1 + if lastByteToWrite > lastMemoryAddr || lastByteToWrite < prog.Vaddr { + return empty, fmt.Errorf("program %d out of memory range: %x - %x (size: %x)", i, prog.Vaddr, lastByteToWrite, prog.Memsz) } - if prog.Vaddr+prog.Memsz >= HEAP_START { - return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) + if lastByteToWrite >= HEAP_START { + return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, lastByteToWrite, prog.Memsz) } if err := s.GetMemory().SetMemoryRange(Word(prog.Vaddr), r); err != nil { return empty, fmt.Errorf("failed to read program segment %d: %w", i, err) diff --git a/cannon/mipsevm/program/load_test.go b/cannon/mipsevm/program/load_test.go new file mode 100644 index 000000000000..32660a7f655c --- /dev/null +++ b/cannon/mipsevm/program/load_test.go @@ -0,0 +1,83 @@ +package program + +import ( + "debug/elf" + "io" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/program/testutil" +) + +func TestLoadELF(t *testing.T) { + data := []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} + dataSize := uint64(len(data)) + lastValidAddr := uint64(HEAP_START - 1) + lastAddr := uint64(^uint32(0)) + if !arch.IsMips32 { + lastAddr = (1 << 48) - 1 + } + + tests := []struct { + name string + progType elf.ProgType + memSize uint64 + fileSize uint64 + vAddr uint64 + expectedErr string + shouldIgnore bool + }{ + {name: "Zero length segment", progType: elf.PT_LOAD, fileSize: 0, memSize: 0, vAddr: 0}, + {name: "Zero length segment, non-zero fileSize", progType: elf.PT_LOAD, fileSize: 2, memSize: 0, vAddr: 0, expectedErr: "file size (2) > mem size (0)"}, + {name: "Loadable segment, fileSize > memSize", progType: elf.PT_LOAD, fileSize: dataSize * 2, memSize: dataSize, vAddr: 0x4000, expectedErr: "file size (16) > mem size (8)"}, + {name: "Loadable segment, fileSize < memSize", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize * 2, vAddr: 0x4000}, + {name: "Loadable segment, fileSize == memSize", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: 0x4000}, + {name: "Loadable segment, segment out-of-range", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastAddr - 1, expectedErr: "out of memory range"}, + {name: "Loadable segment, segment just out-of-range", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastAddr - dataSize + 2, expectedErr: "out of memory range"}, + {name: "Loadable segment, segment just in-range", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastAddr - dataSize + 1, expectedErr: "overlaps with heap"}, + {name: "Loadable segment, segment overlaps heap", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastValidAddr - 1, expectedErr: "overlaps with heap"}, + {name: "Loadable segment, segment just overlaps heap", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastValidAddr - dataSize + 2, expectedErr: "overlaps with heap"}, + {name: "Loadable segment, segment ends just before heap", progType: elf.PT_LOAD, fileSize: dataSize, memSize: dataSize, vAddr: lastValidAddr - dataSize + 1}, + {name: "MIPS Flags segment, invalid file size", progType: elf.PT_MIPS_ABIFLAGS, fileSize: dataSize * 2, memSize: dataSize, vAddr: 0x4000, shouldIgnore: true}, + {name: "MIPS Flags segment, out-of-range", progType: elf.PT_MIPS_ABIFLAGS, fileSize: dataSize, memSize: dataSize, vAddr: lastAddr, shouldIgnore: true}, + {name: "MIPS Flags segment, overlaps heap", progType: elf.PT_MIPS_ABIFLAGS, fileSize: dataSize, memSize: dataSize, vAddr: lastValidAddr, shouldIgnore: true}, + {name: "Other segment, fileSize > memSize", progType: elf.PT_DYNAMIC, fileSize: dataSize * 2, memSize: dataSize, vAddr: 0x4000, expectedErr: "filling for non PT_LOAD segments is not supported"}, + {name: "Other segment, memSize > fileSize", progType: elf.PT_DYNAMIC, fileSize: dataSize, memSize: dataSize * 2, vAddr: 0x4000, expectedErr: "filling for non PT_LOAD segments is not supported"}, + {name: "Other segment, out-of-range", progType: elf.PT_DYNAMIC, fileSize: dataSize, memSize: dataSize, vAddr: lastAddr, expectedErr: "out of memory range"}, + {name: "Other segment, overlaps heap", progType: elf.PT_DYNAMIC, fileSize: dataSize, memSize: dataSize, vAddr: lastValidAddr, expectedErr: "overlaps with heap"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + prog, reader := testutil.MockProgWithReader(tt.progType, tt.fileSize, tt.memSize, tt.vAddr, data) + progs := []*elf.Prog{prog} + mockFile := testutil.MockELFFile(progs) + state, err := LoadELF(mockFile, testutil.MockCreateInitState) + + if tt.expectedErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr) + } else { + require.NoError(t, err) + + if tt.shouldIgnore { + // No data should be read + require.Equal(t, reader.BytesRead, 0) + } else { + // Set up memory validation data + expectedData := make([]byte, tt.memSize) + copy(expectedData, data[:]) + memReader := state.GetMemory().ReadMemoryRange(arch.Word(tt.vAddr), arch.Word(tt.memSize)) + actualData, err := io.ReadAll(memReader) + require.NoError(t, err) + + // Validate data was read into memory + require.Equal(t, reader.BytesRead, int(tt.fileSize)) + require.Equal(t, actualData, expectedData) + } + } + }) + } +} diff --git a/cannon/mipsevm/program/testutil/mocks.go b/cannon/mipsevm/program/testutil/mocks.go new file mode 100644 index 000000000000..484527710aa5 --- /dev/null +++ b/cannon/mipsevm/program/testutil/mocks.go @@ -0,0 +1,130 @@ +package testutil + +import ( + "debug/elf" + "io" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" +) + +// MockELFFile create a mock ELF file with custom program segments +func MockELFFile(progs []*elf.Prog) *elf.File { + return &elf.File{Progs: progs} +} + +// MockProg sets up a elf.Prog structure for testing +func MockProg(progType elf.ProgType, filesz, memsz, vaddr uint64) *elf.Prog { + return &elf.Prog{ + ProgHeader: elf.ProgHeader{ + Type: progType, + Filesz: filesz, + Memsz: memsz, + Vaddr: vaddr, + }, + } +} + +// MockProgWithReader creates an elf.Prog with a TrackableReaderAt to track reads +func MockProgWithReader(progType elf.ProgType, filesz, memsz, vaddr uint64, data []byte) (*elf.Prog, *TrackableReaderAt) { + reader := &TrackableReaderAt{data: data} + prog := MockProg(progType, filesz, memsz, vaddr) + prog.ReaderAt = io.NewSectionReader(reader, 0, int64(filesz)) + return prog, reader +} + +// TrackableReaderAt tracks the number of bytes read +type TrackableReaderAt struct { + data []byte + BytesRead int +} + +func (r *TrackableReaderAt) ReadAt(p []byte, offset int64) (int, error) { + if offset >= int64(len(r.data)) { + return 0, io.EOF + } + numBytesRead := copy(p, r.data[offset:]) + r.BytesRead += numBytesRead + if numBytesRead < len(p) { + return numBytesRead, io.EOF + } + return numBytesRead, nil +} + +// MockCreateInitState returns a mock FPVMState for testing +func MockCreateInitState(pc, heapStart arch.Word) *MockFPVMState { + return newMockFPVMState() +} + +type MockFPVMState struct { + memory *memory.Memory +} + +var _ mipsevm.FPVMState = (*MockFPVMState)(nil) + +func newMockFPVMState() *MockFPVMState { + mem := memory.NewMemory() + state := MockFPVMState{mem} + return &state +} + +func (m MockFPVMState) Serialize(out io.Writer) error { + panic("not implemented") +} + +func (m MockFPVMState) GetMemory() *memory.Memory { + return m.memory +} + +func (m MockFPVMState) GetHeap() arch.Word { + panic("not implemented") +} + +func (m MockFPVMState) GetPreimageKey() common.Hash { + panic("not implemented") +} + +func (m MockFPVMState) GetPreimageOffset() arch.Word { + panic("not implemented") +} + +func (m MockFPVMState) GetPC() arch.Word { + panic("not implemented") +} + +func (m MockFPVMState) GetCpu() mipsevm.CpuScalars { + panic("not implemented") +} + +func (m MockFPVMState) GetRegistersRef() *[32]arch.Word { + panic("not implemented") +} + +func (m MockFPVMState) GetStep() uint64 { + panic("not implemented") +} + +func (m MockFPVMState) GetExited() bool { + panic("not implemented") +} + +func (m MockFPVMState) GetExitCode() uint8 { + panic("not implemented") +} + +func (m MockFPVMState) GetLastHint() hexutil.Bytes { + panic("not implemented") +} + +func (m MockFPVMState) EncodeWitness() (witness []byte, hash common.Hash) { + panic("not implemented") +} + +func (m MockFPVMState) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM { + panic("not implemented") +} From dbaedac2c74090b7d0aec4a97d6d09a04987dc51 Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Mon, 28 Oct 2024 21:51:24 +0100 Subject: [PATCH 064/451] op-node/rollup/derive: Implement pipeline stage multiplexing (#12506) * op-node/rollup/derive: Implement pipeline stage multiplexing * fix BatchStage empty batch generation * fix fork configuration in LargeL1Gaps test --- op-chain-ops/genesis/config.go | 76 ++++++++++- op-chain-ops/genesis/config_test.go | 59 +++++++- op-e2e/actions/derivation/blocktime_test.go | 9 +- op-e2e/actions/helpers/env.go | 83 +++++++++++ op-e2e/actions/helpers/l1_miner.go | 9 +- op-e2e/actions/helpers/l2_sequencer.go | 37 +++-- op-e2e/actions/proofs/helpers/matrix.go | 7 +- .../proofs/sequence_window_expiry_test.go | 9 +- op-e2e/actions/upgrades/helpers/config.go | 20 ++- op-e2e/actions/upgrades/holocene_fork_test.go | 129 ++++++++++++++++++ op-node/rollup/chain_spec.go | 56 ++++++-- op-node/rollup/derive/attributes_queue.go | 10 +- op-node/rollup/derive/batch_mux.go | 76 +++++++++++ op-node/rollup/derive/batch_mux_test.go | 68 +++++++++ op-node/rollup/derive/batch_queue.go | 11 +- op-node/rollup/derive/batch_stage.go | 5 +- op-node/rollup/derive/channel_assembler.go | 10 +- .../rollup/derive/channel_assembler_test.go | 14 +- op-node/rollup/derive/channel_bank.go | 10 +- op-node/rollup/derive/channel_bank_test.go | 80 ++++++----- op-node/rollup/derive/channel_in_reader.go | 12 +- op-node/rollup/derive/channel_mux.go | 74 ++++++++++ op-node/rollup/derive/channel_mux_test.go | 69 ++++++++++ op-node/rollup/derive/frame_queue.go | 24 +++- op-node/rollup/derive/pipeline.go | 24 +++- op-node/rollup/types.go | 11 ++ op-node/rollup/types_test.go | 16 +++ 27 files changed, 879 insertions(+), 129 deletions(-) create mode 100644 op-e2e/actions/helpers/env.go create mode 100644 op-e2e/actions/upgrades/holocene_fork_test.go create mode 100644 op-node/rollup/derive/batch_mux.go create mode 100644 op-node/rollup/derive/batch_mux_test.go create mode 100644 op-node/rollup/derive/channel_mux.go create mode 100644 op-node/rollup/derive/channel_mux_test.go diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index a150a8a7dafe..d89c124e6d31 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -369,6 +369,81 @@ func offsetToUpgradeTime(offset *hexutil.Uint64, genesisTime uint64) *uint64 { return &v } +func (d *UpgradeScheduleDeployConfig) ForkTimeOffset(fork rollup.ForkName) *uint64 { + switch fork { + case rollup.Regolith: + return (*uint64)(d.L2GenesisRegolithTimeOffset) + case rollup.Canyon: + return (*uint64)(d.L2GenesisCanyonTimeOffset) + case rollup.Delta: + return (*uint64)(d.L2GenesisDeltaTimeOffset) + case rollup.Ecotone: + return (*uint64)(d.L2GenesisEcotoneTimeOffset) + case rollup.Fjord: + return (*uint64)(d.L2GenesisFjordTimeOffset) + case rollup.Granite: + return (*uint64)(d.L2GenesisGraniteTimeOffset) + case rollup.Holocene: + return (*uint64)(d.L2GenesisHoloceneTimeOffset) + case rollup.Interop: + return (*uint64)(d.L2GenesisInteropTimeOffset) + default: + panic(fmt.Sprintf("unknown fork: %s", fork)) + } +} + +func (d *UpgradeScheduleDeployConfig) SetForkTimeOffset(fork rollup.ForkName, offset *uint64) { + switch fork { + case rollup.Regolith: + d.L2GenesisRegolithTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Canyon: + d.L2GenesisCanyonTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Delta: + d.L2GenesisDeltaTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Ecotone: + d.L2GenesisEcotoneTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Fjord: + d.L2GenesisFjordTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Granite: + d.L2GenesisGraniteTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Holocene: + d.L2GenesisHoloceneTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Interop: + d.L2GenesisInteropTimeOffset = (*hexutil.Uint64)(offset) + default: + panic(fmt.Sprintf("unknown fork: %s", fork)) + } +} + +var scheduleableForks = rollup.ForksFrom(rollup.Regolith) + +// ActivateForkAtOffset activates the given fork at the given offset. Previous forks are activated +// at genesis and later forks are deactivated. +// If multiple forks should be activated at a later time than genesis, first call +// ActivateForkAtOffset with the earliest fork and then SetForkTimeOffset to individually set later +// forks. +func (d *UpgradeScheduleDeployConfig) ActivateForkAtOffset(fork rollup.ForkName, offset uint64) { + if !rollup.IsValidFork(fork) || fork == rollup.Bedrock { + panic(fmt.Sprintf("invalid fork: %s", fork)) + } + ts := new(uint64) + for i, f := range scheduleableForks { + if f == fork { + d.SetForkTimeOffset(fork, &offset) + ts = nil + } else { + d.SetForkTimeOffset(scheduleableForks[i], ts) + } + } +} + +// ActivateForkAtGenesis activates the given fork, and all previous forks, at genesis. +// Later forks are deactivated. +// See also [ActivateForkAtOffset]. +func (d *UpgradeScheduleDeployConfig) ActivateForkAtGenesis(fork rollup.ForkName) { + d.ActivateForkAtOffset(fork, 0) +} + func (d *UpgradeScheduleDeployConfig) RegolithTime(genesisTime uint64) *uint64 { return offsetToUpgradeTime(d.L2GenesisRegolithTimeOffset, genesisTime) } @@ -402,7 +477,6 @@ func (d *UpgradeScheduleDeployConfig) InteropTime(genesisTime uint64) *uint64 { } func (d *UpgradeScheduleDeployConfig) AllocMode(genesisTime uint64) L2AllocsMode { - forks := d.forks() for i := len(forks) - 1; i >= 0; i-- { if forkTime := offsetToUpgradeTime(forks[i].L2GenesisTimeOffset, genesisTime); forkTime != nil && *forkTime == 0 { diff --git a/op-chain-ops/genesis/config_test.go b/op-chain-ops/genesis/config_test.go index d272d7afd2e5..47ffc4650822 100644 --- a/op-chain-ops/genesis/config_test.go +++ b/op-chain-ops/genesis/config_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/testlog" ) @@ -45,7 +46,10 @@ func TestRegolithTimeZero(t *testing.T) { config := &DeployConfig{ L2InitializationConfig: L2InitializationConfig{ UpgradeScheduleDeployConfig: UpgradeScheduleDeployConfig{ - L2GenesisRegolithTimeOffset: ®olithOffset}}} + L2GenesisRegolithTimeOffset: ®olithOffset, + }, + }, + } require.Equal(t, uint64(0), *config.RegolithTime(1234)) } @@ -54,7 +58,10 @@ func TestRegolithTimeAsOffset(t *testing.T) { config := &DeployConfig{ L2InitializationConfig: L2InitializationConfig{ UpgradeScheduleDeployConfig: UpgradeScheduleDeployConfig{ - L2GenesisRegolithTimeOffset: ®olithOffset}}} + L2GenesisRegolithTimeOffset: ®olithOffset, + }, + }, + } require.Equal(t, uint64(1500+5000), *config.RegolithTime(5000)) } @@ -63,7 +70,10 @@ func TestCanyonTimeZero(t *testing.T) { config := &DeployConfig{ L2InitializationConfig: L2InitializationConfig{ UpgradeScheduleDeployConfig: UpgradeScheduleDeployConfig{ - L2GenesisCanyonTimeOffset: &canyonOffset}}} + L2GenesisCanyonTimeOffset: &canyonOffset, + }, + }, + } require.Equal(t, uint64(0), *config.CanyonTime(1234)) } @@ -72,7 +82,10 @@ func TestCanyonTimeOffset(t *testing.T) { config := &DeployConfig{ L2InitializationConfig: L2InitializationConfig{ UpgradeScheduleDeployConfig: UpgradeScheduleDeployConfig{ - L2GenesisCanyonTimeOffset: &canyonOffset}}} + L2GenesisCanyonTimeOffset: &canyonOffset, + }, + }, + } require.Equal(t, uint64(1234+1500), *config.CanyonTime(1234)) } @@ -124,3 +137,41 @@ func TestL1Deployments(t *testing.T) { // One that doesn't exist returns empty string require.Equal(t, "", deployments.GetName(common.Address{19: 0xff})) } + +// This test guarantees that getters and setters for all forks are present. +func TestUpgradeScheduleDeployConfig_ForkGettersAndSetters(t *testing.T) { + var d UpgradeScheduleDeployConfig + for i, fork := range rollup.ForksFrom(rollup.Regolith) { + require.Nil(t, d.ForkTimeOffset(fork)) + offset := uint64(i * 42) + d.SetForkTimeOffset(fork, &offset) + require.Equal(t, offset, *d.ForkTimeOffset(fork)) + } +} + +func TestUpgradeScheduleDeployConfig_ActivateForkAtOffset(t *testing.T) { + var d UpgradeScheduleDeployConfig + ts := uint64(42) + t.Run("invalid", func(t *testing.T) { + require.Panics(t, func() { d.ActivateForkAtOffset(rollup.Bedrock, ts) }) + }) + + t.Run("regolith", func(t *testing.T) { + d.ActivateForkAtOffset(rollup.Regolith, ts) + require.EqualValues(t, &ts, d.L2GenesisRegolithTimeOffset) + for _, fork := range scheduleableForks[1:] { + require.Nil(t, d.ForkTimeOffset(fork)) + } + }) + + t.Run("ecotone", func(t *testing.T) { + d.ActivateForkAtOffset(rollup.Ecotone, ts) + require.EqualValues(t, &ts, d.L2GenesisEcotoneTimeOffset) + for _, fork := range scheduleableForks[:3] { + require.Zero(t, *d.ForkTimeOffset(fork)) + } + for _, fork := range scheduleableForks[4:] { + require.Nil(t, d.ForkTimeOffset(fork)) + } + }) +} diff --git a/op-e2e/actions/derivation/blocktime_test.go b/op-e2e/actions/derivation/blocktime_test.go index 1855013aad6d..bd5594864af5 100644 --- a/op-e2e/actions/derivation/blocktime_test.go +++ b/op-e2e/actions/derivation/blocktime_test.go @@ -7,6 +7,7 @@ import ( actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" upgradesHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/upgrades/helpers" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -163,11 +164,13 @@ func LargeL1Gaps(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { dp.DeployConfig.L2BlockTime = 2 dp.DeployConfig.SequencerWindowSize = 4 dp.DeployConfig.MaxSequencerDrift = 32 - dp.DeployConfig.L2GenesisEcotoneTimeOffset = nil - dp.DeployConfig.L2GenesisFjordTimeOffset = nil + if deltaTimeOffset != nil { + dp.DeployConfig.ActivateForkAtOffset(rollup.Delta, uint64(*deltaTimeOffset)) + } else { + dp.DeployConfig.ActivateForkAtGenesis(rollup.Canyon) + } // TODO(client-pod#831): The Ecotone (and Fjord) activation blocks don't include user txs, // so disabling these forks for now. - upgradesHelpers.ApplyDeltaTimeOffset(dp, deltaTimeOffset) sd := e2eutils.Setup(t, dp, actionsHelpers.DefaultAlloc) log := testlog.Logger(t, log.LevelDebug) diff --git a/op-e2e/actions/helpers/env.go b/op-e2e/actions/helpers/env.go new file mode 100644 index 000000000000..1bc270f4843f --- /dev/null +++ b/op-e2e/actions/helpers/env.go @@ -0,0 +1,83 @@ +package helpers + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +type Env struct { + Log log.Logger + Logs *testlog.CapturingHandler + + SetupData *e2eutils.SetupData + + Miner *L1Miner + Seq *L2Sequencer + SeqEngine *L2Engine + Verifier *L2Verifier + VerifEngine *L2Engine + Batcher *L2Batcher +} + +type EnvOpt struct { + DeployConfigMod func(*genesis.DeployConfig) +} + +func WithActiveFork(fork rollup.ForkName, offset uint64) EnvOpt { + return EnvOpt{ + DeployConfigMod: func(d *genesis.DeployConfig) { + d.ActivateForkAtOffset(fork, offset) + }, + } +} + +func WithActiveGenesisFork(fork rollup.ForkName) EnvOpt { + return WithActiveFork(fork, 0) +} + +// DefaultFork specifies the default fork to use when setting up the action test environment. +// Currently manually set to Holocene. +// Replace with `var DefaultFork = func() rollup.ForkName { return rollup.AllForks[len(rollup.AllForks)-1] }()` after Interop launch. +const DefaultFork = rollup.Holocene + +// SetupEnv sets up a default action test environment. If no fork is specified, the default fork as +// specified by the package variable [defaultFork] is used. +func SetupEnv(t StatefulTesting, opts ...EnvOpt) (env Env) { + dp := e2eutils.MakeDeployParams(t, DefaultRollupTestParams()) + + log, logs := testlog.CaptureLogger(t, log.LevelDebug) + env.Log, env.Logs = log, logs + + dp.DeployConfig.ActivateForkAtGenesis(DefaultFork) + for _, opt := range opts { + if dcMod := opt.DeployConfigMod; dcMod != nil { + dcMod(dp.DeployConfig) + } + } + + sd := e2eutils.Setup(t, dp, DefaultAlloc) + env.SetupData = sd + env.Miner, env.SeqEngine, env.Seq = SetupSequencerTest(t, sd, log) + env.Miner.ActL1SetFeeRecipient(common.Address{'A'}) + env.VerifEngine, env.Verifier = SetupVerifier(t, sd, log, env.Miner.L1Client(t, sd.RollupCfg), env.Miner.BlobStore(), &sync.Config{}) + rollupSeqCl := env.Seq.RollupClient() + env.Batcher = NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, env.Miner.EthClient(), env.SeqEngine.EthClient(), env.SeqEngine.EngineClient(t, sd.RollupCfg)) + + return +} + +func (env Env) ActBatchSubmitAllAndMine(t Testing) (l1InclusionBlock *types.Block) { + env.Batcher.ActSubmitAll(t) + batchTx := env.Batcher.LastSubmitted + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(batchTx.Hash())(t) + return env.Miner.ActL1EndBlock(t) +} diff --git a/op-e2e/actions/helpers/l1_miner.go b/op-e2e/actions/helpers/l1_miner.go index bf1bb415fe4f..108c11d3e166 100644 --- a/op-e2e/actions/helpers/l1_miner.go +++ b/op-e2e/actions/helpers/l1_miner.go @@ -203,10 +203,10 @@ func (s *L1Miner) ActL1SetFeeRecipient(coinbase common.Address) { } // ActL1EndBlock finishes the new L1 block, and applies it to the chain as unsafe block -func (s *L1Miner) ActL1EndBlock(t Testing) { +func (s *L1Miner) ActL1EndBlock(t Testing) *types.Block { if !s.l1Building { t.InvalidAction("cannot end L1 block when not building block") - return + return nil } s.l1Building = false @@ -253,11 +253,12 @@ func (s *L1Miner) ActL1EndBlock(t Testing) { if err != nil { t.Fatalf("failed to insert block into l1 chain") } + return block } -func (s *L1Miner) ActEmptyBlock(t Testing) { +func (s *L1Miner) ActEmptyBlock(t Testing) *types.Block { s.ActL1StartBlock(12)(t) - s.ActL1EndBlock(t) + return s.ActL1EndBlock(t) } func (s *L1Miner) Close() error { diff --git a/op-e2e/actions/helpers/l2_sequencer.go b/op-e2e/actions/helpers/l2_sequencer.go index 8afed4fd32ce..23fe7b0686f8 100644 --- a/op-e2e/actions/helpers/l2_sequencer.go +++ b/op-e2e/actions/helpers/l2_sequencer.go @@ -52,7 +52,8 @@ type L2Sequencer struct { func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, blobSrc derive.L1BlobsFetcher, altDASrc driver.AltDAIface, eng L2API, cfg *rollup.Config, seqConfDepth uint64, - interopBackend interop.InteropBackend) *L2Sequencer { + interopBackend interop.InteropBackend, +) *L2Sequencer { ver := NewL2Verifier(t, log, l1, blobSrc, altDASrc, eng, cfg, &sync.Config{}, safedb.Disabled, interopBackend) attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, eng) seqConfDepthL1 := confdepth.NewConfDepth(seqConfDepth, ver.syncStatus.L1Head, l1) @@ -130,6 +131,11 @@ func (s *L2Sequencer) ActL2EndBlock(t Testing) { "sync status must be accurate after block building") } +func (s *L2Sequencer) ActL2EmptyBlock(t Testing) { + s.ActL2StartBlock(t) + s.ActL2EndBlock(t) +} + // ActL2KeepL1Origin makes the sequencer use the current L1 origin, even if the next origin is available. func (s *L2Sequencer) ActL2KeepL1Origin(t Testing) { parent := s.engine.UnsafeL2Head() @@ -143,8 +149,7 @@ func (s *L2Sequencer) ActL2KeepL1Origin(t Testing) { func (s *L2Sequencer) ActBuildToL1Head(t Testing) { for s.engine.UnsafeL2Head().L1Origin.Number < s.syncStatus.L1Head().Number { s.ActL2PipelineFull(t) - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } @@ -152,8 +157,7 @@ func (s *L2Sequencer) ActBuildToL1Head(t Testing) { func (s *L2Sequencer) ActBuildToL1HeadUnsafe(t Testing) { for s.engine.UnsafeL2Head().L1Origin.Number < s.syncStatus.L1Head().Number { // Note: the derivation pipeline does not run, we are just sequencing a block on top of the existing L2 chain. - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } @@ -166,8 +170,7 @@ func (s *L2Sequencer) ActBuildToL1HeadExcl(t Testing) { if nextOrigin.Number >= s.syncStatus.L1Head().Number { break } - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } @@ -180,44 +183,40 @@ func (s *L2Sequencer) ActBuildToL1HeadExclUnsafe(t Testing) { if nextOrigin.Number >= s.syncStatus.L1Head().Number { break } - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } func (s *L2Sequencer) ActBuildL2ToTime(t Testing, target uint64) { for s.L2Unsafe().Time < target { - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } func (s *L2Sequencer) ActBuildL2ToEcotone(t Testing) { require.NotNil(t, s.RollupCfg.EcotoneTime, "cannot activate Ecotone when it is not scheduled") for s.L2Unsafe().Time < *s.RollupCfg.EcotoneTime { - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } + func (s *L2Sequencer) ActBuildL2ToFjord(t Testing) { require.NotNil(t, s.RollupCfg.FjordTime, "cannot activate FjordTime when it is not scheduled") for s.L2Unsafe().Time < *s.RollupCfg.FjordTime { - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } + func (s *L2Sequencer) ActBuildL2ToGranite(t Testing) { require.NotNil(t, s.RollupCfg.GraniteTime, "cannot activate GraniteTime when it is not scheduled") for s.L2Unsafe().Time < *s.RollupCfg.GraniteTime { - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } func (s *L2Sequencer) ActBuildL2ToHolocene(t Testing) { require.NotNil(t, s.RollupCfg.HoloceneTime, "cannot activate HoloceneTime when it is not scheduled") for s.L2Unsafe().Time < *s.RollupCfg.HoloceneTime { - s.ActL2StartBlock(t) - s.ActL2EndBlock(t) + s.ActL2EmptyBlock(t) } } diff --git a/op-e2e/actions/proofs/helpers/matrix.go b/op-e2e/actions/proofs/helpers/matrix.go index 2b4ed5a0ded2..ff6bf2ad77b6 100644 --- a/op-e2e/actions/proofs/helpers/matrix.go +++ b/op-e2e/actions/proofs/helpers/matrix.go @@ -89,9 +89,12 @@ var ( Granite = &Hardfork{Name: "Granite", Precedence: 6} Holocene = &Hardfork{Name: "Holocene", Precedence: 7} ) -var Hardforks = ForkMatrix{Regolith, Canyon, Delta, Ecotone, Fjord, Granite, Holocene} -var LatestForkOnly = ForkMatrix{Hardforks[len(Hardforks)-1]} +var ( + Hardforks = ForkMatrix{Regolith, Canyon, Delta, Ecotone, Fjord, Granite, Holocene} + LatestFork = Hardforks[len(Hardforks)-1] + LatestForkOnly = ForkMatrix{LatestFork} +) func NewForkMatrix(forks ...*Hardfork) ForkMatrix { return append(ForkMatrix{}, forks...) diff --git a/op-e2e/actions/proofs/sequence_window_expiry_test.go b/op-e2e/actions/proofs/sequence_window_expiry_test.go index 3f5ca9562d4b..cb702fe8eb6a 100644 --- a/op-e2e/actions/proofs/sequence_window_expiry_test.go +++ b/op-e2e/actions/proofs/sequence_window_expiry_test.go @@ -132,17 +132,18 @@ func Test_ProgramAction_SequenceWindowExpired(gt *testing.T) { matrix := helpers.NewMatrix[any]() defer matrix.Run(gt) + forks := helpers.ForkMatrix{helpers.Granite, helpers.LatestFork} matrix.AddTestCase( "HonestClaim", nil, - helpers.LatestForkOnly, + forks, runSequenceWindowExpireTest, helpers.ExpectNoError(), ) matrix.AddTestCase( "JunkClaim", nil, - helpers.LatestForkOnly, + forks, runSequenceWindowExpireTest, helpers.ExpectError(claim.ErrClaimNotValid), helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), @@ -150,14 +151,14 @@ func Test_ProgramAction_SequenceWindowExpired(gt *testing.T) { matrix.AddTestCase( "ChannelCloseAfterWindowExpiry-HonestClaim", nil, - helpers.LatestForkOnly, + forks, runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test, helpers.ExpectNoError(), ) matrix.AddTestCase( "ChannelCloseAfterWindowExpiry-JunkClaim", nil, - helpers.LatestForkOnly, + forks, runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test, helpers.ExpectError(claim.ErrClaimNotValid), helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), diff --git a/op-e2e/actions/upgrades/helpers/config.go b/op-e2e/actions/upgrades/helpers/config.go index 1c844afd3265..c09d0be48b6c 100644 --- a/op-e2e/actions/upgrades/helpers/config.go +++ b/op-e2e/actions/upgrades/helpers/config.go @@ -8,7 +8,6 @@ import ( // ApplyDeltaTimeOffset adjusts fork configuration to not conflict with the delta overrides func ApplyDeltaTimeOffset(dp *e2eutils.DeployParams, deltaTimeOffset *hexutil.Uint64) { dp.DeployConfig.L2GenesisDeltaTimeOffset = deltaTimeOffset - dp.DeployConfig.L2GenesisGraniteTimeOffset = nil // configure Ecotone to not be before Delta accidentally if dp.DeployConfig.L2GenesisEcotoneTimeOffset != nil { if deltaTimeOffset == nil { @@ -17,6 +16,7 @@ func ApplyDeltaTimeOffset(dp *e2eutils.DeployParams, deltaTimeOffset *hexutil.Ui dp.DeployConfig.L2GenesisEcotoneTimeOffset = deltaTimeOffset } } + // configure Fjord to not be before Delta accidentally if dp.DeployConfig.L2GenesisFjordTimeOffset != nil { if deltaTimeOffset == nil { @@ -25,4 +25,22 @@ func ApplyDeltaTimeOffset(dp *e2eutils.DeployParams, deltaTimeOffset *hexutil.Ui dp.DeployConfig.L2GenesisFjordTimeOffset = deltaTimeOffset } } + + // configure Granite to not be before Delta accidentally + if dp.DeployConfig.L2GenesisGraniteTimeOffset != nil { + if deltaTimeOffset == nil { + dp.DeployConfig.L2GenesisGraniteTimeOffset = nil + } else if *dp.DeployConfig.L2GenesisGraniteTimeOffset < *deltaTimeOffset { + dp.DeployConfig.L2GenesisGraniteTimeOffset = deltaTimeOffset + } + } + + // configure Holocene to not be before Delta accidentally + if dp.DeployConfig.L2GenesisHoloceneTimeOffset != nil { + if deltaTimeOffset == nil { + dp.DeployConfig.L2GenesisHoloceneTimeOffset = nil + } else if *dp.DeployConfig.L2GenesisHoloceneTimeOffset < *deltaTimeOffset { + dp.DeployConfig.L2GenesisHoloceneTimeOffset = deltaTimeOffset + } + } } diff --git a/op-e2e/actions/upgrades/holocene_fork_test.go b/op-e2e/actions/upgrades/holocene_fork_test.go new file mode 100644 index 000000000000..d9145b3d50ff --- /dev/null +++ b/op-e2e/actions/upgrades/holocene_fork_test.go @@ -0,0 +1,129 @@ +package upgrades + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/stretchr/testify/require" +) + +func TestHoloceneActivationAtGenesis(gt *testing.T) { + t := helpers.NewDefaultTesting(gt) + env := helpers.SetupEnv(t, helpers.WithActiveGenesisFork(rollup.Holocene)) + + // Start op-nodes + env.Seq.ActL2PipelineFull(t) + env.Verifier.ActL2PipelineFull(t) + + // Verify Holocene is active at genesis + l2Head := env.Seq.L2Unsafe() + require.NotZero(t, l2Head.Hash) + require.True(t, env.SetupData.RollupCfg.IsHolocene(l2Head.Time), "Holocene should be active at genesis") + + // build empty L1 block + env.Miner.ActEmptyBlock(t) + + // Build L2 chain and advance safe head + env.Seq.ActL1HeadSignal(t) + env.Seq.ActBuildToL1Head(t) + + // verify in logs that correct stage got activated + recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter("activating Holocene stage during reset"), testlog.NewAttributesFilter("role", e2esys.RoleSeq)) + require.Len(t, recs, 2) + recs = env.Logs.FindLogs(testlog.NewMessageContainsFilter("activating Holocene stage during reset"), testlog.NewAttributesFilter("role", e2esys.RoleVerif)) + require.Len(t, recs, 2) + + env.ActBatchSubmitAllAndMine(t) + + // verifier picks up the L2 chain that was submitted + env.Verifier.ActL1HeadSignal(t) + env.Verifier.ActL2PipelineFull(t) + require.Equal(t, env.Verifier.L2Safe(), env.Seq.L2Unsafe(), "verifier syncs from sequencer via L1") + require.NotEqual(t, env.Seq.L2Safe(), env.Seq.L2Unsafe(), "sequencer has not processed L1 yet") +} + +func TestHoloceneLateActivationAndReset(gt *testing.T) { + t := helpers.NewDefaultTesting(gt) + holoceneOffset := uint64(24) + env := helpers.SetupEnv(t, helpers.WithActiveFork(rollup.Holocene, holoceneOffset)) + + requireHoloceneTransformationLogs := func(role string, expNumLogs int) { + recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter("transforming to Holocene"), testlog.NewAttributesFilter("role", role)) + require.Len(t, recs, expNumLogs) + if expNumLogs > 0 { + fqRecs := env.Logs.FindLogs(testlog.NewMessageFilter("FrameQueue: resetting with Holocene activation"), testlog.NewAttributesFilter("role", role)) + require.Len(t, fqRecs, 1) + } + } + + requirePreHoloceneActivationLogs := func(role string, expNumLogs int) { + recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter("activating pre-Holocene stage during reset"), testlog.NewAttributesFilter("role", role)) + require.Len(t, recs, expNumLogs) + } + + // Start op-nodes + env.Seq.ActL2PipelineFull(t) + env.Verifier.ActL2PipelineFull(t) + + // Verify Holocene is not active at genesis yet + l2Head := env.Seq.L2Unsafe() + require.NotZero(t, l2Head.Hash) + require.True(t, env.SetupData.RollupCfg.IsGranite(l2Head.Time), "Granite should be active at genesis") + require.False(t, env.SetupData.RollupCfg.IsHolocene(l2Head.Time), "Holocene should not be active at genesis") + + requirePreHoloceneActivationLogs(e2esys.RoleSeq, 2) + requirePreHoloceneActivationLogs(e2esys.RoleVerif, 2) + // Verify no stage transformations took place yet + requireHoloceneTransformationLogs(e2esys.RoleSeq, 0) + requireHoloceneTransformationLogs(e2esys.RoleVerif, 0) + + env.Seq.ActL2EmptyBlock(t) + l1PreHolocene := env.ActBatchSubmitAllAndMine(t) + require.False(t, env.SetupData.RollupCfg.IsHolocene(l1PreHolocene.Time()), + "Holocene should not be active at the first L1 inclusion block") + + // Build a few L2 blocks. We only need the L1 inclusion to advance past Holocene and Holocene + // shouldn't activate with L2 time. + env.Seq.ActBuildL2ToHolocene(t) + + // verify in logs that stage transformations hasn't happened yet, activates by L1 inclusion block + requireHoloceneTransformationLogs(e2esys.RoleSeq, 0) + requireHoloceneTransformationLogs(e2esys.RoleVerif, 0) + + // Submit L2 + l1Head := env.ActBatchSubmitAllAndMine(t) + require.True(t, env.SetupData.RollupCfg.IsHolocene(l1Head.Time())) + + // verifier picks up the L2 chain that was submitted + env.Verifier.ActL1HeadSignal(t) + env.Verifier.ActL2PipelineFull(t) + l2Safe := env.Verifier.L2Safe() + require.Equal(t, l2Safe, env.Seq.L2Unsafe(), "verifier syncs from sequencer via L1") + require.NotEqual(t, env.Seq.L2Safe(), env.Seq.L2Unsafe(), "sequencer has not processed L1 yet") + require.True(t, env.SetupData.RollupCfg.IsHolocene(l2Safe.Time), "Holocene should now be active") + requireHoloceneTransformationLogs(e2esys.RoleSeq, 0) + requireHoloceneTransformationLogs(e2esys.RoleVerif, 2) + + // sequencer also picks up L2 safe chain + env.Seq.ActL1HeadSignal(t) + env.Seq.ActL2PipelineFull(t) + requireHoloceneTransformationLogs(e2esys.RoleSeq, 2) + require.Equal(t, env.Seq.L2Safe(), env.Seq.L2Unsafe(), "sequencer has processed L1") + + // reorg L1 without batch submission + env.Miner.ActL1RewindToParent(t) + env.Miner.ActEmptyBlock(t) + env.Miner.ActEmptyBlock(t) + + env.Seq.ActL1HeadSignal(t) + env.Verifier.ActL1HeadSignal(t) + env.Seq.ActL2PipelineFull(t) + env.Verifier.ActL2PipelineFull(t) + + // duplicate activation logs + requirePreHoloceneActivationLogs(e2esys.RoleSeq, 4) + requirePreHoloceneActivationLogs(e2esys.RoleVerif, 4) +} diff --git a/op-node/rollup/chain_spec.go b/op-node/rollup/chain_spec.go index 66d2e526d0d1..1ddcb3190290 100644 --- a/op-node/rollup/chain_spec.go +++ b/op-node/rollup/chain_spec.go @@ -1,6 +1,7 @@ package rollup import ( + "fmt" "math/big" "github.com/ethereum-optimism/optimism/op-node/params" @@ -41,19 +42,47 @@ const ( Granite ForkName = "granite" Holocene ForkName = "holocene" Interop ForkName = "interop" - None ForkName = "none" + // ADD NEW FORKS TO AllForks BELOW! + None ForkName = "none" ) -var nextFork = map[ForkName]ForkName{ - Bedrock: Regolith, - Regolith: Canyon, - Canyon: Delta, - Delta: Ecotone, - Ecotone: Fjord, - Fjord: Granite, - Granite: Holocene, - Holocene: Interop, - Interop: None, +var AllForks = []ForkName{ + Bedrock, + Regolith, + Canyon, + Delta, + Ecotone, + Fjord, + Granite, + Holocene, + Interop, + // ADD NEW FORKS HERE! +} + +func ForksFrom(fork ForkName) []ForkName { + for i, f := range AllForks { + if f == fork { + return AllForks[i:] + } + } + panic(fmt.Sprintf("invalid fork: %s", fork)) +} + +var nextFork = func() map[ForkName]ForkName { + m := make(map[ForkName]ForkName, len(AllForks)) + for i, f := range AllForks { + if i == len(AllForks)-1 { + m[f] = None + break + } + m[f] = AllForks[i+1] + } + return m +}() + +func IsValidFork(fork ForkName) bool { + _, ok := nextFork[fork] + return ok } type ChainSpec struct { @@ -80,6 +109,11 @@ func (s *ChainSpec) IsCanyon(t uint64) bool { return s.config.IsCanyon(t) } +// IsHolocene returns true if t >= holocene_time +func (s *ChainSpec) IsHolocene(t uint64) bool { + return s.config.IsHolocene(t) +} + // MaxChannelBankSize returns the maximum number of bytes the can allocated inside the channel bank // before pruning occurs at the given timestamp. func (s *ChainSpec) MaxChannelBankSize(t uint64) uint64 { diff --git a/op-node/rollup/derive/attributes_queue.go b/op-node/rollup/derive/attributes_queue.go index c15079c6bfa9..0111ba29c8e5 100644 --- a/op-node/rollup/derive/attributes_queue.go +++ b/op-node/rollup/derive/attributes_queue.go @@ -39,12 +39,18 @@ type AttributesQueue struct { log log.Logger config *rollup.Config builder AttributesBuilder - prev *BatchQueue + prev SingularBatchProvider batch *SingularBatch isLastInSpan bool } -func NewAttributesQueue(log log.Logger, cfg *rollup.Config, builder AttributesBuilder, prev *BatchQueue) *AttributesQueue { +type SingularBatchProvider interface { + ResettableStage + Origin() eth.L1BlockRef + NextBatch(context.Context, eth.L2BlockRef) (*SingularBatch, bool, error) +} + +func NewAttributesQueue(log log.Logger, cfg *rollup.Config, builder AttributesBuilder, prev SingularBatchProvider) *AttributesQueue { return &AttributesQueue{ log: log, config: cfg, diff --git a/op-node/rollup/derive/batch_mux.go b/op-node/rollup/derive/batch_mux.go new file mode 100644 index 000000000000..ea8336a59b1e --- /dev/null +++ b/op-node/rollup/derive/batch_mux.go @@ -0,0 +1,76 @@ +package derive + +import ( + "context" + "fmt" + + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slices" +) + +// BatchMux multiplexes between different batch stages. +// Stages are swapped on demand during Reset calls, or explicitly with Transform. +// It currently chooses the BatchQueue pre-Holocene and the BatchStage post-Holocene. +type BatchMux struct { + log log.Logger + cfg *rollup.Config + prev NextBatchProvider + l2 SafeBlockFetcher + + // embedded active stage + SingularBatchProvider +} + +var _ SingularBatchProvider = (*BatchMux)(nil) + +// NewBatchMux returns an uninitialized BatchMux. Reset has to be called before +// calling other methods, to activate the right stage for a given L1 origin. +func NewBatchMux(lgr log.Logger, cfg *rollup.Config, prev NextBatchProvider, l2 SafeBlockFetcher) *BatchMux { + return &BatchMux{log: lgr, cfg: cfg, prev: prev, l2: l2} +} + +func (b *BatchMux) Reset(ctx context.Context, base eth.L1BlockRef, sysCfg eth.SystemConfig) error { + // TODO(12490): change to a switch over b.cfg.ActiveFork(base.Time) + switch { + default: + if _, ok := b.SingularBatchProvider.(*BatchQueue); !ok { + b.log.Info("BatchMux: activating pre-Holocene stage during reset", "origin", base) + b.SingularBatchProvider = NewBatchQueue(b.log, b.cfg, b.prev, b.l2) + } + case b.cfg.IsHolocene(base.Time): + if _, ok := b.SingularBatchProvider.(*BatchStage); !ok { + b.log.Info("BatchMux: activating Holocene stage during reset", "origin", base) + b.SingularBatchProvider = NewBatchStage(b.log, b.cfg, b.prev, b.l2) + } + } + return b.SingularBatchProvider.Reset(ctx, base, sysCfg) +} + +func (b *BatchMux) Transform(f rollup.ForkName) { + switch f { + case rollup.Holocene: + b.TransformHolocene() + } +} + +func (b *BatchMux) TransformHolocene() { + switch bp := b.SingularBatchProvider.(type) { + case *BatchQueue: + b.log.Info("BatchMux: transforming to Holocene stage") + bs := NewBatchStage(b.log, b.cfg, b.prev, b.l2) + // Even though any ongoing span batch or queued batches are dropped at Holocene activation, the + // post-Holocene batch stage still needs access to the collected l1Blocks pre-Holocene because + // the first Holocene channel will contain pre-Holocene batches. + bs.l1Blocks = slices.Clone(bp.l1Blocks) + bs.origin = bp.origin + b.SingularBatchProvider = bs + case *BatchStage: + // Even if the pipeline is Reset to the activation block, the previous origin will be the + // same, so transfromStages isn't called. + panic(fmt.Sprintf("Holocene BatchStage already active, old origin: %v", bp.Origin())) + default: + panic(fmt.Sprintf("unknown batch stage type: %T", bp)) + } +} diff --git a/op-node/rollup/derive/batch_mux_test.go b/op-node/rollup/derive/batch_mux_test.go new file mode 100644 index 000000000000..2afc25a69dc2 --- /dev/null +++ b/op-node/rollup/derive/batch_mux_test.go @@ -0,0 +1,68 @@ +package derive + +import ( + "context" + "io" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func TestBatchMux_LaterHolocene(t *testing.T) { + log := testlog.Logger(t, log.LevelTrace) + ctx := context.Background() + l1A := eth.L1BlockRef{Time: 0, Hash: common.Hash{0xaa}} + l1B := eth.L1BlockRef{Time: 12, Hash: common.Hash{0xbb}} + cfg := &rollup.Config{ + HoloceneTime: &l1B.Time, + } + b := NewBatchMux(log, cfg, nil, nil) + + require.Nil(t, b.SingularBatchProvider) + + err := b.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(BatchQueue), b.SingularBatchProvider) + require.Equal(t, l1A, b.SingularBatchProvider.(*BatchQueue).origin) + + b.Transform(rollup.Holocene) + require.IsType(t, new(BatchStage), b.SingularBatchProvider) + require.Equal(t, l1A, b.SingularBatchProvider.(*BatchStage).origin) + + err = b.Reset(ctx, l1B, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(BatchStage), b.SingularBatchProvider) + require.Equal(t, l1B, b.SingularBatchProvider.(*BatchStage).origin) + + err = b.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(BatchQueue), b.SingularBatchProvider) + require.Equal(t, l1A, b.SingularBatchProvider.(*BatchQueue).origin) +} + +func TestBatchMux_ActiveHolocene(t *testing.T) { + log := testlog.Logger(t, log.LevelTrace) + ctx := context.Background() + l1A := eth.L1BlockRef{Time: 42, Hash: common.Hash{0xaa}} + cfg := &rollup.Config{ + HoloceneTime: &l1A.Time, + } + // without the fake input, the panic check later would panic because of the Origin() call + prev := &fakeBatchQueueInput{origin: l1A} + b := NewBatchMux(log, cfg, prev, nil) + + require.Nil(t, b.SingularBatchProvider) + + err := b.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(BatchStage), b.SingularBatchProvider) + require.Equal(t, l1A, b.SingularBatchProvider.(*BatchStage).origin) + + require.Panics(t, func() { b.Transform(rollup.Holocene) }) +} diff --git a/op-node/rollup/derive/batch_queue.go b/op-node/rollup/derive/batch_queue.go index b2f27ea27f3a..25199297144e 100644 --- a/op-node/rollup/derive/batch_queue.go +++ b/op-node/rollup/derive/batch_queue.go @@ -49,6 +49,8 @@ type baseBatchStage struct { log log.Logger config *rollup.Config prev NextBatchProvider + l2 SafeBlockFetcher + origin eth.L1BlockRef // l1Blocks contains consecutive eth.L1BlockRef sorted by time. @@ -61,8 +63,6 @@ type baseBatchStage struct { // nextSpan is cached SingularBatches derived from SpanBatch nextSpan []*SingularBatch - - l2 SafeBlockFetcher } func newBaseBatchStage(log log.Logger, cfg *rollup.Config, prev NextBatchProvider, l2 SafeBlockFetcher) baseBatchStage { @@ -86,11 +86,6 @@ func (bs *baseBatchStage) Log() log.Logger { } } -type SingularBatchProvider interface { - ResettableStage - NextBatch(context.Context, eth.L2BlockRef) (*SingularBatch, bool, error) -} - // BatchQueue contains a set of batches for every L1 block. // L1 blocks are contiguous and this does not support reorgs. type BatchQueue struct { @@ -262,10 +257,10 @@ func (bs *baseBatchStage) reset(base eth.L1BlockRef) { // Copy over the Origin from the next stage // It is set in the engine queue (two stages away) such that the L2 Safe Head origin is the progress bs.origin = base + bs.l1Blocks = bs.l1Blocks[:0] // Include the new origin as an origin to build on // Note: This is only for the initialization case. During normal resets we will later // throw out this block. - bs.l1Blocks = bs.l1Blocks[:0] bs.l1Blocks = append(bs.l1Blocks, base) bs.nextSpan = bs.nextSpan[:0] } diff --git a/op-node/rollup/derive/batch_stage.go b/op-node/rollup/derive/batch_stage.go index 22fdbc157158..4e4b171b4c9e 100644 --- a/op-node/rollup/derive/batch_stage.go +++ b/op-node/rollup/derive/batch_stage.go @@ -15,6 +15,8 @@ type BatchStage struct { baseBatchStage } +var _ SingularBatchProvider = (*BatchStage)(nil) + func NewBatchStage(log log.Logger, cfg *rollup.Config, prev NextBatchProvider, l2 SafeBlockFetcher) *BatchStage { return &BatchStage{baseBatchStage: newBaseBatchStage(log, cfg, prev, l2)} } @@ -68,7 +70,8 @@ func (bs *BatchStage) NextBatch(ctx context.Context, parent eth.L2BlockRef) (*Si // We only consider empty batch generation after we've drained all batches from the local // span batch queue and the previous stage. empty, err := bs.deriveNextEmptyBatch(ctx, true, parent) - return empty, false, err + // An empty batch always advances the safe head. + return empty, true, err } else if err != nil { return nil, false, err } diff --git a/op-node/rollup/derive/channel_assembler.go b/op-node/rollup/derive/channel_assembler.go index 6d1424f46a32..6252bd217b71 100644 --- a/op-node/rollup/derive/channel_assembler.go +++ b/op-node/rollup/derive/channel_assembler.go @@ -20,16 +20,16 @@ type ChannelAssembler struct { prev NextFrameProvider } -var _ ResettableStage = (*ChannelAssembler)(nil) +var _ RawChannelProvider = (*ChannelAssembler)(nil) type ChannelStageSpec interface { ChannelTimeout(t uint64) uint64 MaxRLPBytesPerChannel(t uint64) uint64 } -// NewChannelStage creates a Holocene ChannelStage. -// It must only be used for derivation from Holocene activation. -func NewChannelStage(log log.Logger, spec ChannelStageSpec, prev NextFrameProvider, m Metrics) *ChannelAssembler { +// NewChannelAssembler creates the Holocene channel stage. +// It must only be used for derivation from Holocene origins. +func NewChannelAssembler(log log.Logger, spec ChannelStageSpec, prev NextFrameProvider, m Metrics) *ChannelAssembler { return &ChannelAssembler{ log: log, spec: spec, @@ -60,7 +60,7 @@ func (ca *ChannelAssembler) channelTimedOut() bool { return ca.channel.OpenBlockNumber()+ca.spec.ChannelTimeout(ca.Origin().Time) < ca.Origin().Number } -func (ca *ChannelAssembler) NextData(ctx context.Context) ([]byte, error) { +func (ca *ChannelAssembler) NextRawChannel(ctx context.Context) ([]byte, error) { if ca.channel != nil && ca.channelTimedOut() { ca.metrics.RecordChannelTimedOut() ca.resetChannel() diff --git a/op-node/rollup/derive/channel_assembler_test.go b/op-node/rollup/derive/channel_assembler_test.go index 77dc1d87f7e3..75429e5bae58 100644 --- a/op-node/rollup/derive/channel_assembler_test.go +++ b/op-node/rollup/derive/channel_assembler_test.go @@ -110,11 +110,11 @@ func TestChannelStage_NextData(t *testing.T) { MaxRLPBytesPerChannelOverride: tc.rlpOverride, } - cs := NewChannelStage(lgr, spec, fq, metrics.NoopMetrics) + cs := NewChannelAssembler(lgr, spec, fq, metrics.NoopMetrics) for i, fs := range tc.frames { fq.AddFrames(fs...) - data, err := cs.NextData(context.Background()) + data, err := cs.NextRawChannel(context.Background()) require.Equal(t, tc.expData[i], string(data)) require.ErrorIs(t, tc.expErr[i], err) // invariant: never holds a ready channel @@ -129,7 +129,7 @@ func TestChannelStage_NextData(t *testing.T) { } // final call should always be io.EOF after exhausting frame queue - data, err := cs.NextData(context.Background()) + data, err := cs.NextRawChannel(context.Background()) require.Nil(t, data) require.Equal(t, io.EOF, err) }) @@ -141,10 +141,10 @@ func TestChannelStage_NextData_Timeout(t *testing.T) { fq := &fakeChannelBankInput{} lgr := testlog.Logger(t, slog.LevelWarn) spec := rollup.NewChainSpec(&rollup.Config{GraniteTime: ptr(uint64(0))}) // const channel timeout - cs := NewChannelStage(lgr, spec, fq, metrics.NoopMetrics) + cs := NewChannelAssembler(lgr, spec, fq, metrics.NoopMetrics) fq.AddFrames("a:0:foo") - data, err := cs.NextData(context.Background()) + data, err := cs.NextRawChannel(context.Background()) require.Nil(data) require.Equal(io.EOF, err) require.NotNil(cs.channel) @@ -153,7 +153,7 @@ func TestChannelStage_NextData_Timeout(t *testing.T) { // move close to timeout fq.origin.Number = spec.ChannelTimeout(0) fq.AddFrames("a:1:bar") - data, err = cs.NextData(context.Background()) + data, err = cs.NextRawChannel(context.Background()) require.Nil(data) require.Equal(io.EOF, err) require.NotNil(cs.channel) @@ -162,7 +162,7 @@ func TestChannelStage_NextData_Timeout(t *testing.T) { // timeout channel by moving origin past timeout fq.origin.Number = spec.ChannelTimeout(0) + 1 fq.AddFrames("a:2:baz!") - data, err = cs.NextData(context.Background()) + data, err = cs.NextRawChannel(context.Background()) require.Nil(data) require.Equal(io.EOF, err) require.Nil(cs.channel) diff --git a/op-node/rollup/derive/channel_bank.go b/op-node/rollup/derive/channel_bank.go index 39582d2712fa..f26cb34b7fcf 100644 --- a/op-node/rollup/derive/channel_bank.go +++ b/op-node/rollup/derive/channel_bank.go @@ -40,13 +40,13 @@ type ChannelBank struct { prev NextFrameProvider } -var _ ResettableStage = (*ChannelBank)(nil) +var _ RawChannelProvider = (*ChannelBank)(nil) // NewChannelBank creates a ChannelBank, which should be Reset(origin) before use. -func NewChannelBank(log log.Logger, cfg *rollup.Config, prev NextFrameProvider, m Metrics) *ChannelBank { +func NewChannelBank(log log.Logger, spec *rollup.ChainSpec, prev NextFrameProvider, m Metrics) *ChannelBank { return &ChannelBank{ log: log, - spec: rollup.NewChainSpec(cfg), + spec: spec, metrics: m, channels: make(map[ChannelID]*Channel), channelQueue: make([]ChannelID, 0, 10), @@ -170,12 +170,12 @@ func (cb *ChannelBank) tryReadChannelAtIndex(i int) (data []byte, err error) { return data, nil } -// NextData pulls the next piece of data from the channel bank. +// NextRawChannel pulls the next piece of data from the channel bank. // Note that it attempts to pull data out of the channel bank prior to // loading data in (unlike most other stages). This is to ensure maintain // consistency around channel bank pruning which depends upon the order // of operations. -func (cb *ChannelBank) NextData(ctx context.Context) ([]byte, error) { +func (cb *ChannelBank) NextRawChannel(ctx context.Context) ([]byte, error) { // Do the read from the channel bank first data, err := cb.Read() if err == io.EOF { diff --git a/op-node/rollup/derive/channel_bank_test.go b/op-node/rollup/derive/channel_bank_test.go index 75b7503400b1..7b0baddf2373 100644 --- a/op-node/rollup/derive/channel_bank_test.go +++ b/op-node/rollup/derive/channel_bank_test.go @@ -106,32 +106,31 @@ func TestChannelBankSimple(t *testing.T) { input.AddFrames("a:0:first", "a:2:third!") input.AddFrames("a:1:second") - cfg := &rollup.Config{ChannelTimeoutBedrock: 10} - - cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), cfg, input, metrics.NoopMetrics) + spec := rollup.NewChainSpec(&rollup.Config{ChannelTimeoutBedrock: 10}) + cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), spec, input, metrics.NoopMetrics) // Load the first frame - out, err := cb.NextData(context.Background()) + out, err := cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load the third frame - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load the second frame - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Pull out the channel data - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "firstsecondthird", string(out)) // No more data - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, out) require.Equal(t, io.EOF, err) } @@ -149,52 +148,51 @@ func TestChannelBankInterleavedPreCanyon(t *testing.T) { input.AddFrames("b:0:premiere") input.AddFrames("a:1:second") - cfg := &rollup.Config{ChannelTimeoutBedrock: 10, CanyonTime: nil} - - cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), cfg, input, metrics.NoopMetrics) + spec := rollup.NewChainSpec(&rollup.Config{ChannelTimeoutBedrock: 10}) + cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), spec, input, metrics.NoopMetrics) // Load a:0 - out, err := cb.NextData(context.Background()) + out, err := cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:2 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:1 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load a:2 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:0 & Channel b is complete, but channel a was opened first - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load a:1 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Pull out the channel a - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "firstsecondthird", string(out)) // Pull out the channel b - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "premieredeuxtrois", string(out)) // No more data - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, out) require.Equal(t, io.EOF, err) } @@ -213,52 +211,51 @@ func TestChannelBankInterleaved(t *testing.T) { input.AddFrames("a:1:second") ct := uint64(0) - cfg := &rollup.Config{ChannelTimeoutBedrock: 10, CanyonTime: &ct} - - cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), cfg, input, metrics.NoopMetrics) + spec := rollup.NewChainSpec(&rollup.Config{ChannelTimeoutBedrock: 10, CanyonTime: &ct}) + cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), spec, input, metrics.NoopMetrics) // Load a:0 - out, err := cb.NextData(context.Background()) + out, err := cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:2 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:1 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load a:2 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load b:0 & Channel b is complete. Channel a was opened first but isn't ready - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Pull out the channel b because it's ready first. - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "premieredeuxtrois", string(out)) // Load a:1 - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Pull out the channel a - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "firstsecondthird", string(out)) // No more data - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, out) require.Equal(t, io.EOF, err) } @@ -272,40 +269,39 @@ func TestChannelBankDuplicates(t *testing.T) { input.AddFrames("a:0:altfirst", "a:2:altthird!") input.AddFrames("a:1:second") - cfg := &rollup.Config{ChannelTimeoutBedrock: 10} - - cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), cfg, input, metrics.NoopMetrics) + spec := rollup.NewChainSpec(&rollup.Config{ChannelTimeoutBedrock: 10}) + cb := NewChannelBank(testlog.Logger(t, log.LevelCrit), spec, input, metrics.NoopMetrics) // Load the first frame - out, err := cb.NextData(context.Background()) + out, err := cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load the third frame - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load the duplicate frames - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Load the second frame - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.ErrorIs(t, err, NotEnoughData) require.Equal(t, []byte(nil), out) // Pull out the channel data. Expect to see the original set & not the duplicates - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, err) require.Equal(t, "firstsecondthird", string(out)) // No more data - out, err = cb.NextData(context.Background()) + out, err = cb.NextRawChannel(context.Background()) require.Nil(t, out) require.Equal(t, io.EOF, err) } diff --git a/op-node/rollup/derive/channel_in_reader.go b/op-node/rollup/derive/channel_in_reader.go index 2aabf6bc9fdd..6310fba600c9 100644 --- a/op-node/rollup/derive/channel_in_reader.go +++ b/op-node/rollup/derive/channel_in_reader.go @@ -21,7 +21,7 @@ type ChannelInReader struct { spec *rollup.ChainSpec cfg *rollup.Config nextBatchFn func() (*BatchData, error) - prev *ChannelBank + prev RawChannelProvider metrics Metrics } @@ -30,8 +30,14 @@ var ( _ ChannelFlusher = (*ChannelInReader)(nil) ) +type RawChannelProvider interface { + ResettableStage + Origin() eth.L1BlockRef + NextRawChannel(ctx context.Context) ([]byte, error) +} + // NewChannelInReader creates a ChannelInReader, which should be Reset(origin) before use. -func NewChannelInReader(cfg *rollup.Config, log log.Logger, prev *ChannelBank, metrics Metrics) *ChannelInReader { +func NewChannelInReader(cfg *rollup.Config, log log.Logger, prev RawChannelProvider, metrics Metrics) *ChannelInReader { return &ChannelInReader{ spec: rollup.NewChainSpec(cfg), cfg: cfg, @@ -68,7 +74,7 @@ func (cr *ChannelInReader) NextChannel() { // It will return a temporary error if it needs to be called again to advance some internal state. func (cr *ChannelInReader) NextBatch(ctx context.Context) (Batch, error) { if cr.nextBatchFn == nil { - if data, err := cr.prev.NextData(ctx); err == io.EOF { + if data, err := cr.prev.NextRawChannel(ctx); err == io.EOF { return nil, io.EOF } else if err != nil { return nil, err diff --git a/op-node/rollup/derive/channel_mux.go b/op-node/rollup/derive/channel_mux.go new file mode 100644 index 000000000000..cb5ce66baffe --- /dev/null +++ b/op-node/rollup/derive/channel_mux.go @@ -0,0 +1,74 @@ +package derive + +import ( + "context" + "fmt" + + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/log" +) + +// ChannelMux multiplexes between different channel stages. +// Stages are swapped on demand during Reset calls, or explicitly with Transform. +// It currently chooses the ChannelBank pre-Holocene and the ChannelAssembler post-Holocene. +type ChannelMux struct { + log log.Logger + spec *rollup.ChainSpec + prev NextFrameProvider + m Metrics + + // embedded active stage + RawChannelProvider +} + +var _ RawChannelProvider = (*ChannelMux)(nil) + +// NewChannelMux returns a ChannelMux with the ChannelBank as activated stage. Reset has to be called before +// calling other methods, to activate the right stage for a given L1 origin. +func NewChannelMux(log log.Logger, spec *rollup.ChainSpec, prev NextFrameProvider, m Metrics) *ChannelMux { + return &ChannelMux{ + log: log, + spec: spec, + prev: prev, + m: m, + } +} + +func (c *ChannelMux) Reset(ctx context.Context, base eth.L1BlockRef, sysCfg eth.SystemConfig) error { + // TODO(12490): change to a switch over c.cfg.ActiveFork(base.Time) + switch { + default: + if _, ok := c.RawChannelProvider.(*ChannelBank); !ok { + c.log.Info("ChannelMux: activating pre-Holocene stage during reset", "origin", base) + c.RawChannelProvider = NewChannelBank(c.log, c.spec, c.prev, c.m) + } + case c.spec.IsHolocene(base.Time): + if _, ok := c.RawChannelProvider.(*ChannelAssembler); !ok { + c.log.Info("ChannelMux: activating Holocene stage during reset", "origin", base) + c.RawChannelProvider = NewChannelAssembler(c.log, c.spec, c.prev, c.m) + } + } + return c.RawChannelProvider.Reset(ctx, base, sysCfg) +} + +func (c *ChannelMux) Transform(f rollup.ForkName) { + switch f { + case rollup.Holocene: + c.TransformHolocene() + } +} + +func (c *ChannelMux) TransformHolocene() { + switch cp := c.RawChannelProvider.(type) { + case *ChannelBank: + c.log.Info("ChannelMux: transforming to Holocene stage") + c.RawChannelProvider = NewChannelAssembler(c.log, c.spec, c.prev, c.m) + case *ChannelAssembler: + // Even if the pipeline is Reset to the activation block, the previous origin will be the + // same, so transfromStages isn't called. + panic(fmt.Sprintf("Holocene ChannelAssembler already active, old origin: %v", cp.Origin())) + default: + panic(fmt.Sprintf("unknown channel stage type: %T", cp)) + } +} diff --git a/op-node/rollup/derive/channel_mux_test.go b/op-node/rollup/derive/channel_mux_test.go new file mode 100644 index 000000000000..59fd669922ae --- /dev/null +++ b/op-node/rollup/derive/channel_mux_test.go @@ -0,0 +1,69 @@ +package derive + +import ( + "context" + "io" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-node/metrics" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func TestChannelMux_LaterHolocene(t *testing.T) { + log := testlog.Logger(t, log.LevelTrace) + ctx := context.Background() + l1A := eth.L1BlockRef{Time: 0, Hash: common.Hash{0xaa}} + l1B := eth.L1BlockRef{Time: 12, Hash: common.Hash{0xbb}} + cfg := &rollup.Config{ + HoloceneTime: &l1B.Time, + } + spec := rollup.NewChainSpec(cfg) + m := metrics.NoopMetrics + c := NewChannelMux(log, spec, nil, m) + + require.Nil(t, c.RawChannelProvider) + + err := c.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(ChannelBank), c.RawChannelProvider) + + c.Transform(rollup.Holocene) + require.IsType(t, new(ChannelAssembler), c.RawChannelProvider) + + err = c.Reset(ctx, l1B, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(ChannelAssembler), c.RawChannelProvider) + + err = c.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(ChannelBank), c.RawChannelProvider) +} + +func TestChannelMux_ActiveHolocene(t *testing.T) { + log := testlog.Logger(t, log.LevelTrace) + ctx := context.Background() + l1A := eth.L1BlockRef{Time: 42, Hash: common.Hash{0xaa}} + cfg := &rollup.Config{ + HoloceneTime: &l1A.Time, + } + spec := rollup.NewChainSpec(cfg) + // without the fake input, the panic check later would panic because of the Origin() call + prev := &fakeChannelBankInput{} + m := metrics.NoopMetrics + c := NewChannelMux(log, spec, prev, m) + + require.Nil(t, c.RawChannelProvider) + + err := c.Reset(ctx, l1A, eth.SystemConfig{}) + require.Equal(t, io.EOF, err) + require.IsType(t, new(ChannelAssembler), c.RawChannelProvider) + + require.Panics(t, func() { c.Transform(rollup.Holocene) }) +} diff --git a/op-node/rollup/derive/frame_queue.go b/op-node/rollup/derive/frame_queue.go index 77a2703290ce..361f1cfda886 100644 --- a/op-node/rollup/derive/frame_queue.go +++ b/op-node/rollup/derive/frame_queue.go @@ -10,7 +10,10 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" ) -var _ NextFrameProvider = &FrameQueue{} +var ( + _ NextFrameProvider = (*FrameQueue)(nil) + _ ForkTransformer = (*FrameQueue)(nil) +) //go:generate mockery --name NextDataProvider --case snake type NextDataProvider interface { @@ -33,13 +36,20 @@ func NewFrameQueue(log log.Logger, cfg *rollup.Config, prev NextDataProvider) *F } } +func (fq *FrameQueue) Transform(f rollup.ForkName) { + switch f { + case rollup.Holocene: + fq.log.Info("FrameQueue: resetting with Holocene activation") + // With Holocene activation, the frame queue is simply reset + fq.reset() + } +} + func (fq *FrameQueue) Origin() eth.L1BlockRef { return fq.prev.Origin() } func (fq *FrameQueue) NextFrame(ctx context.Context) (Frame, error) { - // TODO(12157): reset frame queue once at Holocene L1 origin block - // Only load more frames if necessary if len(fq.frames) == 0 { if err := fq.loadNextFrames(ctx); err != nil { @@ -129,7 +139,11 @@ func pruneFrameQueue(frames []Frame) []Frame { return frames } -func (fq *FrameQueue) Reset(_ context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error { - fq.frames = fq.frames[:0] +func (fq *FrameQueue) Reset(context.Context, eth.L1BlockRef, eth.SystemConfig) error { + fq.reset() return io.EOF } + +func (fq *FrameQueue) reset() { + fq.frames = fq.frames[:0] +} diff --git a/op-node/rollup/derive/pipeline.go b/op-node/rollup/derive/pipeline.go index f114e2a4b0d3..95189b35e100 100644 --- a/op-node/rollup/derive/pipeline.go +++ b/op-node/rollup/derive/pipeline.go @@ -38,6 +38,10 @@ type ResettableStage interface { Reset(ctx context.Context, base eth.L1BlockRef, baseCfg eth.SystemConfig) error } +type ForkTransformer interface { + Transform(rollup.ForkName) +} + type L2Source interface { PayloadByHash(context.Context, common.Hash) (*eth.ExecutionPayloadEnvelope, error) PayloadByNumber(context.Context, uint64) (*eth.ExecutionPayloadEnvelope, error) @@ -79,14 +83,15 @@ type DerivationPipeline struct { func NewDerivationPipeline(log log.Logger, rollupCfg *rollup.Config, l1Fetcher L1Fetcher, l1Blobs L1BlobsFetcher, altDA AltDAInputFetcher, l2Source L2Source, metrics Metrics, ) *DerivationPipeline { + spec := rollup.NewChainSpec(rollupCfg) // Pull stages l1Traversal := NewL1Traversal(log, rollupCfg, l1Fetcher) dataSrc := NewDataSourceFactory(log, rollupCfg, l1Fetcher, l1Blobs, altDA) // auxiliary stage for L1Retrieval l1Src := NewL1Retrieval(log, dataSrc, l1Traversal) frameQueue := NewFrameQueue(log, rollupCfg, l1Src) - bank := NewChannelBank(log, rollupCfg, frameQueue, metrics) + bank := NewChannelMux(log, spec, frameQueue, metrics) chInReader := NewChannelInReader(rollupCfg, log, bank, metrics) - batchQueue := NewBatchQueue(log, rollupCfg, chInReader, l2Source) + batchQueue := NewBatchMux(log, rollupCfg, chInReader, l2Source) attrBuilder := NewFetchingAttributesBuilder(rollupCfg, l1Fetcher, l2Source) attributesQueue := NewAttributesQueue(log, rollupCfg, attrBuilder, batchQueue) @@ -177,6 +182,7 @@ func (dp *DerivationPipeline) Step(ctx context.Context, pendingSafeHead eth.L2Bl if err := VerifyNewL1Origin(ctx, prevOrigin, dp.l1Fetcher, newOrigin); err != nil { return nil, fmt.Errorf("failed to verify L1 origin transition: %w", err) } + dp.transformStages(prevOrigin, newOrigin) dp.origin = newOrigin } @@ -238,6 +244,20 @@ func (dp *DerivationPipeline) initialReset(ctx context.Context, resetL2Safe eth. return nil } +func (db *DerivationPipeline) transformStages(oldOrigin, newOrigin eth.L1BlockRef) { + fork := db.rollupCfg.IsActivationBlock(oldOrigin.Time, newOrigin.Time) + if fork == "" { + return + } + + db.log.Info("Transforming stages", "fork", fork) + for _, stage := range db.stages { + if tf, ok := stage.(ForkTransformer); ok { + tf.Transform(fork) + } + } +} + func (dp *DerivationPipeline) ConfirmEngineReset() { dp.engineIsReset = true } diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 42036ba372e9..e5e541a6f7a5 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -465,6 +465,17 @@ func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool { !c.IsInterop(l2BlockTime-c.BlockTime) } +// IsActivationBlock returns the fork which activates at the block with time newTime if the previous +// block's time is oldTime. It return an empty ForkName if no fork activation takes place between +// those timestamps. It can be used for both, L1 and L2 blocks. +// TODO(12490): Currently only supports Holocene. Will be modularized in a follow-up. +func (c *Config) IsActivationBlock(oldTime, newTime uint64) ForkName { + if c.IsHolocene(newTime) && !c.IsHolocene(oldTime) { + return Holocene + } + return "" +} + func (c *Config) ActivateAtGenesis(hardfork ForkName) { // IMPORTANT! ordered from newest to oldest switch hardfork { diff --git a/op-node/rollup/types_test.go b/op-node/rollup/types_test.go index 4b2575afa391..11c4db505c96 100644 --- a/op-node/rollup/types_test.go +++ b/op-node/rollup/types_test.go @@ -701,3 +701,19 @@ func TestGetPayloadVersion(t *testing.T) { }) } } + +func TestConfig_IsActivationBlock(t *testing.T) { + ts := uint64(42) + // TODO(12490): Currently only supports Holocene. Will be modularized in a follow-up. + for _, fork := range []ForkName{Holocene} { + cfg := &Config{ + HoloceneTime: &ts, + } + require.Equal(t, fork, cfg.IsActivationBlock(0, ts)) + require.Equal(t, fork, cfg.IsActivationBlock(0, ts+64)) + require.Equal(t, fork, cfg.IsActivationBlock(ts-1, ts)) + require.Equal(t, fork, cfg.IsActivationBlock(ts-1, ts+1)) + require.Zero(t, cfg.IsActivationBlock(0, ts-1)) + require.Zero(t, cfg.IsActivationBlock(ts, ts+1)) + } +} From eeb5c5a008aadcbf879d37988f7a435ee65faa91 Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 17:28:29 -0700 Subject: [PATCH 065/451] cannon: Update stf verify image (#12706) --- cannon/Dockerfile.diff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cannon/Dockerfile.diff b/cannon/Dockerfile.diff index dcdd3587e681..20664a25139b 100644 --- a/cannon/Dockerfile.diff +++ b/cannon/Dockerfile.diff @@ -23,7 +23,7 @@ ARG GIT_DATE ARG TARGETOS TARGETARCH -FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.1 AS cannon-v2 +FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.2 AS cannon-v2 FROM --platform=$BUILDPLATFORM builder as cannon-verify COPY --from=cannon-v2 /usr/local/bin/cannon /usr/local/bin/cannon-v2 From 0797d147bb7408cec54e4c9a505210f6fdd8b713 Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 18:43:57 -0700 Subject: [PATCH 066/451] proofs-tools: Update challenger (#12707) --- docker-bake.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-bake.hcl b/docker-bake.hcl index 50feeba66d45..66a36b294bec 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -206,7 +206,7 @@ target "proofs-tools" { dockerfile = "./ops/docker/proofs-tools/Dockerfile" context = "." args = { - CHALLENGER_VERSION="22d8365199b3141fcfbccc7cb9e107a71e151b0a" + CHALLENGER_VERSION="005116d9b4e39c7ba0586ce95ceba4067123efd5" KONA_VERSION="kona-client-v0.1.0-alpha.5" } target="proofs-tools" From cc42cc55749cc108ff396fe8086c09c2b464c1e0 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 29 Oct 2024 14:10:52 +1000 Subject: [PATCH 067/451] signer: Restore default of enabling TLS. (#12713) Can be disabled with --signer.tls.enabled=false. Avoids breaking existing deployments that default to TLS enabled. --- op-challenger/cmd/main_test.go | 12 ++++++++++++ op-service/tls/cli.go | 26 +++++++++++++++++--------- op-service/tls/cli_test.go | 7 +++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index d471f45dc594..d960fa607059 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -937,6 +937,18 @@ func TestAdditionalBondClaimants(t *testing.T) { }) } +func TestSignerTLS(t *testing.T) { + t.Run("EnabledByDefault", func(t *testing.T) { + cfg := configForArgs(t, addRequiredArgs(types.TraceTypeAlphabet)) + require.True(t, cfg.TxMgrConfig.SignerCLIConfig.TLSConfig.Enabled) + }) + + t.Run("Disabled", func(t *testing.T) { + cfg := configForArgs(t, addRequiredArgs(types.TraceTypeAlphabet, "--signer.tls.enabled=false")) + require.False(t, cfg.TxMgrConfig.SignerCLIConfig.TLSConfig.Enabled) + }) +} + func verifyArgsInvalid(t *testing.T, messageContains string, cliArgs []string) { _, _, err := dryRunWithArgs(cliArgs) require.ErrorContains(t, err, messageContains) diff --git a/op-service/tls/cli.go b/op-service/tls/cli.go index b00b084cd339..e2e086e922c3 100644 --- a/op-service/tls/cli.go +++ b/op-service/tls/cli.go @@ -12,9 +12,10 @@ import ( ) const ( - TLSCaCertFlagName = "tls.ca" - TLSCertFlagName = "tls.cert" - TLSKeyFlagName = "tls.key" + TLSCaCertFlagName = "tls.ca" + TLSCertFlagName = "tls.cert" + TLSKeyFlagName = "tls.key" + TLSEnabledFlagName = "tls.enabled" ) // CLIFlags returns flags with env var envPrefix @@ -24,9 +25,10 @@ func CLIFlags(envPrefix string) []cli.Flag { } var ( - defaultTLSCaCert = "tls/ca.crt" - defaultTLSCert = "tls/tls.crt" - defaultTLSKey = "tls/tls.key" + defaultTLSCaCert = "tls/ca.crt" + defaultTLSCert = "tls/tls.crt" + defaultTLSKey = "tls/tls.key" + defaultTLSEnabled = true ) // CLIFlagsWithFlagPrefix returns flags with env var and cli flag prefixes @@ -39,6 +41,12 @@ func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string) []cli.Flag { return opservice.PrefixEnvVar(envPrefix, name) } return []cli.Flag{ + &cli.BoolFlag{ + Name: prefixFunc(TLSEnabledFlagName), + Usage: "Enable or disable TLS client authentication for the signer", + Value: defaultTLSEnabled, + EnvVars: prefixEnvVars("TLS_ENABLED"), + }, &cli.StringFlag{ Name: prefixFunc(TLSCaCertFlagName), Usage: "tls ca cert path", @@ -72,7 +80,7 @@ func NewCLIConfig() CLIConfig { TLSCaCert: defaultTLSCaCert, TLSCert: defaultTLSCert, TLSKey: defaultTLSKey, - Enabled: false, + Enabled: true, } } @@ -95,7 +103,7 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig { TLSCaCert: ctx.String(TLSCaCertFlagName), TLSCert: ctx.String(TLSCertFlagName), TLSKey: ctx.String(TLSKeyFlagName), - Enabled: ctx.IsSet(TLSCaCertFlagName) || ctx.IsSet(TLSCertFlagName) || ctx.IsSet(TLSKeyFlagName), + Enabled: ctx.Bool(TLSEnabledFlagName), } } @@ -109,6 +117,6 @@ func ReadCLIConfigWithPrefix(ctx *cli.Context, flagPrefix string) CLIConfig { TLSCaCert: ctx.String(prefixFunc(TLSCaCertFlagName)), TLSCert: ctx.String(prefixFunc(TLSCertFlagName)), TLSKey: ctx.String(prefixFunc(TLSKeyFlagName)), - Enabled: ctx.IsSet(TLSCaCertFlagName) || ctx.IsSet(TLSCertFlagName) || ctx.IsSet(TLSKeyFlagName), + Enabled: ctx.Bool(prefixFunc(TLSEnabledFlagName)), } } diff --git a/op-service/tls/cli_test.go b/op-service/tls/cli_test.go index f926a19fabdd..bd4ea4bf17c9 100644 --- a/op-service/tls/cli_test.go +++ b/op-service/tls/cli_test.go @@ -1,6 +1,7 @@ package tls import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -41,6 +42,12 @@ func TestInvalidConfig(t *testing.T) { err := cfg.Check() require.ErrorContains(t, err, "all tls flags must be set if at least one is set") }) + t.Run(fmt.Sprintf("%sAllowedWhenDisabled", test.name), func(t *testing.T) { + cfg := NewCLIConfig() + cfg.Enabled = false + test.configChange(&cfg) + require.NoError(t, cfg.Check()) + }) } } From 0d923d3951efc245f0e1f80a959ba9bdc8b0308b Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 29 Oct 2024 14:14:23 +1000 Subject: [PATCH 068/451] ci: Fix proofs-team reference (#12716) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4db56cb4d3d1..22edd29aa5ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1031,7 +1031,7 @@ jobs: paths: - "/root/.cache/go-build" - notify-failures-on-develop: - mentions: "@proofs-squad" + mentions: "@proofs-team" semgrep-scan: parameters: From b46bffed42db3442d7484f089278d59f51503049 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 29 Oct 2024 09:44:45 +0530 Subject: [PATCH 069/451] op-deployer: use dev keys for vaults in new intent (#12679) * fix: use devkeys for vaults in intent * feat: parametrized keys on l2 chain id --- op-deployer/pkg/deployer/init.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op-deployer/pkg/deployer/init.go b/op-deployer/pkg/deployer/init.go index a81ef6f48a24..e8044f75a8c0 100644 --- a/op-deployer/pkg/deployer/init.go +++ b/op-deployer/pkg/deployer/init.go @@ -118,9 +118,9 @@ func Init(cfg InitConfig) error { l2ChainIDBig := l2ChainID.Big() intent.Chains = append(intent.Chains, &state.ChainIntent{ ID: l2ChainID, - BaseFeeVaultRecipient: common.Address{}, - L1FeeVaultRecipient: common.Address{}, - SequencerFeeVaultRecipient: common.Address{}, + BaseFeeVaultRecipient: addrFor(devkeys.BaseFeeVaultRecipientRole.Key(l2ChainIDBig)), + L1FeeVaultRecipient: addrFor(devkeys.L1FeeVaultRecipientRole.Key(l2ChainIDBig)), + SequencerFeeVaultRecipient: addrFor(devkeys.SequencerFeeVaultRecipientRole.Key(l2ChainIDBig)), Eip1559Denominator: 50, Eip1559Elasticity: 6, Roles: state.ChainRoles{ From 1ca85526e8d6a2e48df6589e6f8454d7fd725e4a Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Mon, 28 Oct 2024 23:48:16 -0500 Subject: [PATCH 070/451] Interop: Expanded E2E Tests (#12659) * Expand Interop E2E Testing * fix test ; address comment --- op-e2e/interop/interop_test.go | 223 +++++++++++++----- op-e2e/interop/supersystem.go | 10 +- op-service/sources/supervisor_client.go | 68 ++++-- op-supervisor/supervisor/backend/backend.go | 4 +- op-supervisor/supervisor/backend/db/query.go | 2 +- .../backend/processors/contracts/l2inbox.go | 17 +- .../processors/contracts/l2inbox_test.go | 2 +- .../backend/processors/log_processor.go | 14 +- op-supervisor/supervisor/types/types.go | 13 + 9 files changed, 242 insertions(+), 111 deletions(-) diff --git a/op-e2e/interop/interop_test.go b/op-e2e/interop/interop_test.go index 20428e31e388..6724f468be51 100644 --- a/op-e2e/interop/interop_test.go +++ b/op-e2e/interop/interop_test.go @@ -3,22 +3,24 @@ package interop import ( "context" "math/big" + "sync" "testing" "time" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + gethTypes "github.com/ethereum/go-ethereum/core/types" ) -// TestInteropTrivial tests a simple interop scenario -// Chains A and B exist, but no messages are sent between them -// and in fact no event-logs are emitted by either chain at all. -// A transaction is sent from Alice to Bob on Chain A. -// The balance of Bob on Chain A is checked before and after the tx. -// The balance of Bob on Chain B is checked after the tx. -func TestInteropTrivial(t *testing.T) { +// setupAndRun is a helper function that sets up a SuperSystem +// which contains two L2 Chains, and two users on each chain. +func setupAndRun(t *testing.T, fn func(*testing.T, SuperSystem)) { recipe := interopgen.InteropDevRecipe{ L1ChainID: 900100, L2ChainIDs: []uint64{900200, 900201}, @@ -32,66 +34,163 @@ func TestInteropTrivial(t *testing.T) { // create a super system from the recipe // and get the L2 IDs for use in the test s2 := NewSuperSystem(t, &recipe, worldResources) - ids := s2.L2IDs() - - // chainA is the first L2 chain - chainA := ids[0] - // chainB is the second L2 chain - chainB := ids[1] // create two users on all L2 chains s2.AddUser("Alice") s2.AddUser("Bob") - bobAddr := s2.Address(chainA, "Bob") - - // check the balance of Bob - clientA := s2.L2GethClient(chainA) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - bobBalance, err := clientA.BalanceAt(ctx, bobAddr, nil) - require.NoError(t, err) - expectedBalance, _ := big.NewInt(0).SetString("10000000000000000000000000", 10) - require.Equal(t, expectedBalance, bobBalance) - - // send a tx from Alice to Bob - s2.SendL2Tx( - chainA, - "Alice", - func(l2Opts *helpers.TxOpts) { - l2Opts.ToAddr = &bobAddr - l2Opts.Value = big.NewInt(1000000) - l2Opts.GasFeeCap = big.NewInt(1_000_000_000) - l2Opts.GasTipCap = big.NewInt(1_000_000_000) - }, - ) - - // check the balance of Bob after the tx - ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - bobBalance, err = clientA.BalanceAt(ctx, bobAddr, nil) - require.NoError(t, err) - expectedBalance, _ = big.NewInt(0).SetString("10000000000000000001000000", 10) - require.Equal(t, expectedBalance, bobBalance) - - // check that the balance of Bob on ChainB hasn't changed - bobAddrB := s2.Address(chainB, "Bob") - clientB := s2.L2GethClient(chainB) - ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - bobBalance, err = clientB.BalanceAt(ctx, bobAddrB, nil) - require.NoError(t, err) - expectedBalance, _ = big.NewInt(0).SetString("10000000000000000000000000", 10) - require.Equal(t, expectedBalance, bobBalance) - - s2.DeployEmitterContract(chainA, "Alice") - s2.DeployEmitterContract(chainB, "Alice") - for i := 0; i < 1; i++ { - s2.EmitData(chainA, "Alice", "0x1234567890abcdef") - - s2.EmitData(chainB, "Alice", "0x1234567890abcdef") - } + // run the test + fn(t, s2) +} - time.Sleep(60 * time.Second) +// TestInterop_IsolatedChains tests a simple interop scenario +// Chains A and B exist, but no messages are sent between them +// a transaction is sent from Alice to Bob on Chain A, +// and only Chain A is affected. +func TestInterop_IsolatedChains(t *testing.T) { + test := func(t *testing.T, s2 SuperSystem) { + ids := s2.L2IDs() + chainA := ids[0] + chainB := ids[1] + + // check the balance of Bob + bobAddr := s2.Address(chainA, "Bob") + clientA := s2.L2GethClient(chainA) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + bobBalance, err := clientA.BalanceAt(ctx, bobAddr, nil) + require.NoError(t, err) + expectedBalance, _ := big.NewInt(0).SetString("10000000000000000000000000", 10) + require.Equal(t, expectedBalance, bobBalance) + + // send a tx from Alice to Bob + s2.SendL2Tx( + chainA, + "Alice", + func(l2Opts *helpers.TxOpts) { + l2Opts.ToAddr = &bobAddr + l2Opts.Value = big.NewInt(1000000) + l2Opts.GasFeeCap = big.NewInt(1_000_000_000) + l2Opts.GasTipCap = big.NewInt(1_000_000_000) + }, + ) + + // check the balance of Bob after the tx + ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + bobBalance, err = clientA.BalanceAt(ctx, bobAddr, nil) + require.NoError(t, err) + expectedBalance, _ = big.NewInt(0).SetString("10000000000000000001000000", 10) + require.Equal(t, expectedBalance, bobBalance) + + // check that the balance of Bob on ChainB hasn't changed + bobAddrB := s2.Address(chainB, "Bob") + clientB := s2.L2GethClient(chainB) + ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + bobBalance, err = clientB.BalanceAt(ctx, bobAddrB, nil) + require.NoError(t, err) + expectedBalance, _ = big.NewInt(0).SetString("10000000000000000000000000", 10) + require.Equal(t, expectedBalance, bobBalance) + } + setupAndRun(t, test) +} +// TestInteropTrivial_EmitLogs tests a simple interop scenario +// Chains A and B exist, but no messages are sent between them. +// A contract is deployed on each chain, and logs are emitted repeatedly. +func TestInteropTrivial_EmitLogs(t *testing.T) { + test := func(t *testing.T, s2 SuperSystem) { + ids := s2.L2IDs() + chainA := ids[0] + chainB := ids[1] + EmitterA := s2.DeployEmitterContract(chainA, "Alice") + EmitterB := s2.DeployEmitterContract(chainB, "Alice") + payload1 := "SUPER JACKPOT!" + numEmits := 10 + // emit logs on both chains in parallel + var emitParallel sync.WaitGroup + emitOn := func(chainID string) { + for i := 0; i < numEmits; i++ { + s2.EmitData(chainID, "Alice", payload1) + } + emitParallel.Done() + } + emitParallel.Add(2) + go emitOn(chainA) + go emitOn(chainB) + emitParallel.Wait() + + clientA := s2.L2GethClient(chainA) + clientB := s2.L2GethClient(chainB) + // check that the logs are emitted on chain A + qA := ethereum.FilterQuery{ + Addresses: []common.Address{EmitterA}, + } + logsA, err := clientA.FilterLogs(context.Background(), qA) + require.NoError(t, err) + require.Len(t, logsA, numEmits) + + // check that the logs are emitted on chain B + qB := ethereum.FilterQuery{ + Addresses: []common.Address{EmitterB}, + } + logsB, err := clientB.FilterLogs(context.Background(), qB) + require.NoError(t, err) + require.Len(t, logsB, numEmits) + + // wait for cross-safety to settle + // I've tried 30s but not all logs are cross-safe by then + time.Sleep(60 * time.Second) + + supervisor := s2.SupervisorClient() + + // requireMessage checks the safety level of a log against the supervisor + // it also checks that the error is as expected + requireMessage := func(chainID string, log gethTypes.Log, expectedSafety types.SafetyLevel, expectedError error) { + client := s2.L2GethClient(chainID) + // construct the expected hash of the log's payload + // (topics concatenated with data) + msgPayload := make([]byte, 0) + for _, topic := range log.Topics { + msgPayload = append(msgPayload, topic.Bytes()...) + } + msgPayload = append(msgPayload, log.Data...) + expectedHash := common.BytesToHash(crypto.Keccak256(msgPayload)) + // convert payload hash to log hash + logHash := types.PayloadHashToLogHash(expectedHash, log.Address) + + // get block for the log (for timestamp) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + block, err := client.BlockByHash(ctx, log.BlockHash) + require.NoError(t, err) + + // make an identifier out of the sample log + identifier := types.Identifier{ + Origin: log.Address, + BlockNumber: log.BlockNumber, + LogIndex: uint64(log.Index), + Timestamp: block.Time(), + ChainID: types.ChainIDFromBig(s2.ChainID(chainID)), + } + + safety, error := supervisor.CheckMessage(context.Background(), + identifier, + logHash, + ) + require.ErrorIs(t, error, expectedError) + // the supervisor could progress the safety level more quickly than we expect, + // which is why we check for a minimum safety level + require.True(t, safety.AtLeastAsSafe(expectedSafety), "log: %v should be at least %s, but is %s", log, expectedSafety.String(), safety.String()) + } + // all logs should be cross-safe + for _, log := range logsA { + requireMessage(chainA, log, types.CrossSafe, nil) + } + for _, log := range logsB { + requireMessage(chainB, log, types.CrossSafe, nil) + } + } + setupAndRun(t, test) } diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index 7e9e20519567..2d063174bdae 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -7,6 +7,7 @@ import ( "os" "path" "path/filepath" + "sort" "testing" "time" @@ -79,8 +80,10 @@ type SuperSystem interface { L2GethClient(network string) *ethclient.Client // get the secret for a network and role L2OperatorKey(network string, role devkeys.ChainOperatorRole) ecdsa.PrivateKey - // get the list of network IDs + // get the list of network IDs as key-strings L2IDs() []string + // get the chain ID for a network + ChainID(network string) *big.Int // register a username to an account on all L2s AddUser(username string) // get the user key for a user on an L2 @@ -415,6 +418,10 @@ func (s *interopE2ESystem) newL2(id string, l2Out *interopgen.L2Output) l2Set { } } +func (s *interopE2ESystem) ChainID(network string) *big.Int { + return s.l2s[network].chainID +} + // prepareSupervisor creates a new supervisor for the system func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService { // Be verbose with op-supervisor, it's in early test phase @@ -604,6 +611,7 @@ func (s *interopE2ESystem) L2IDs() []string { for id := range s.l2s { ids = append(ids, id) } + sort.Strings(ids) return ids } diff --git a/op-service/sources/supervisor_client.go b/op-service/sources/supervisor_client.go index d85fc4901f31..ba8663c335ce 100644 --- a/op-service/sources/supervisor_client.go +++ b/op-service/sources/supervisor_client.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/ethereum/go-ethereum/common" ) type SupervisorClient struct { @@ -19,9 +20,7 @@ func NewSupervisorClient(client client.RPC) *SupervisorClient { } } -func (cl *SupervisorClient) Stop( - ctx context.Context, -) error { +func (cl *SupervisorClient) Stop(ctx context.Context) error { var result error err := cl.client.CallContext( ctx, @@ -33,9 +32,7 @@ func (cl *SupervisorClient) Stop( return result } -func (cl *SupervisorClient) Start( - ctx context.Context, -) error { +func (cl *SupervisorClient) Start(ctx context.Context) error { var result error err := cl.client.CallContext( ctx, @@ -47,10 +44,7 @@ func (cl *SupervisorClient) Start( return result } -func (cl *SupervisorClient) AddL2RPC( - ctx context.Context, - rpc string, -) error { +func (cl *SupervisorClient) AddL2RPC(ctx context.Context, rpc string) error { var result error err := cl.client.CallContext( ctx, @@ -63,6 +57,25 @@ func (cl *SupervisorClient) AddL2RPC( return result } +func (cl *SupervisorClient) CheckMessage(ctx context.Context, identifier types.Identifier, logHash common.Hash) (types.SafetyLevel, error) { + var result types.SafetyLevel + err := cl.client.CallContext( + ctx, + &result, + "supervisor_checkMessage", + identifier, + logHash) + if err != nil { + return types.Invalid, fmt.Errorf("failed to check message (chain %s), (block %v), (index %v), (logHash %s): %w", + identifier.ChainID, + identifier.BlockNumber, + identifier.LogIndex, + logHash, + err) + } + return result, nil +} + func (cl *SupervisorClient) UnsafeView(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) { var result types.ReferenceView err := cl.client.CallContext( @@ -93,26 +106,51 @@ func (cl *SupervisorClient) SafeView(ctx context.Context, chainID types.ChainID, func (cl *SupervisorClient) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { var result eth.BlockID - err := cl.client.CallContext(ctx, &result, "supervisor_finalized", chainID) + err := cl.client.CallContext( + ctx, + &result, + "supervisor_finalized", + chainID) return result, err } func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.BlockRef, error) { var result eth.BlockRef - err := cl.client.CallContext(ctx, &result, "supervisor_derivedFrom", chainID, derived) + err := cl.client.CallContext( + ctx, + &result, + "supervisor_derivedFrom", + chainID, + derived) return result, err } func (cl *SupervisorClient) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { - return cl.client.CallContext(ctx, nil, "supervisor_updateLocalUnsafe", chainID, head) + return cl.client.CallContext( + ctx, + nil, + "supervisor_updateLocalUnsafe", + chainID, + head) } func (cl *SupervisorClient) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { - return cl.client.CallContext(ctx, nil, "supervisor_updateLocalSafe", chainID, derivedFrom, lastDerived) + return cl.client.CallContext( + ctx, + nil, + "supervisor_updateLocalSafe", + chainID, + derivedFrom, + lastDerived) } func (cl *SupervisorClient) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalizedL1 eth.L1BlockRef) error { - return cl.client.CallContext(ctx, nil, "supervisor_updateFinalizedL1", chainID, finalizedL1) + return cl.client.CallContext( + ctx, + nil, + "supervisor_updateFinalizedL1", + chainID, + finalizedL1) } func (cl *SupervisorClient) Close() { diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 5768e16e3ca0..a9322b84c057 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -308,14 +308,14 @@ func (su *SupervisorBackend) DependencySet() depset.DependencySet { // Query methods // ---------------------------- -func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) { +func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, logHash common.Hash) (types.SafetyLevel, error) { su.mu.RLock() defer su.mu.RUnlock() chainID := identifier.ChainID blockNum := identifier.BlockNumber logIdx := identifier.LogIndex - _, err := su.chainDBs.Check(chainID, blockNum, uint32(logIdx), payloadHash) + _, err := su.chainDBs.Check(chainID, blockNum, uint32(logIdx), logHash) if errors.Is(err, types.ErrFuture) { return types.LocalUnsafe, nil } diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index 7dc2e72d3e72..01e5ef0171c6 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -358,7 +358,7 @@ func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID // Safest returns the strongest safety level that can be guaranteed for the given log entry. // it assumes the log entry has already been checked and is valid, this function only checks safety levels. -// Cross-safety levels are all considered to be more safe than any form of local-safety. +// Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest. func (db *ChainsDB) Safest(chainID types.ChainID, blockNum uint64, index uint32) (safest types.SafetyLevel, err error) { db.mu.RLock() defer db.mu.RUnlock() diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go index 07c05ac6dc67..3d0eb1a6b3b0 100644 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go +++ b/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots" "github.com/ethereum/go-ethereum/common" ethTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" ) const ( @@ -74,7 +73,7 @@ func (i *CrossL2Inbox) DecodeExecutingMessageLog(l *ethTypes.Log) (types.Executi if err != nil { return types.ExecutingMessage{}, fmt.Errorf("failed to convert chain ID %v to uint32: %w", identifier.ChainId, err) } - hash := payloadHashToLogHash(msgHash, identifier.Origin) + hash := types.PayloadHashToLogHash(msgHash, identifier.Origin) return types.ExecutingMessage{ Chain: types.ChainIndex(chainID), // TODO(#11105): translate chain ID to chain index Hash: hash, @@ -117,17 +116,3 @@ func identifierFromBytes(identifierBytes io.Reader) (contractIdentifier, error) ChainId: chainID, }, nil } - -// payloadHashToLogHash converts the payload hash to the log hash -// it is the concatenation of the log's address and the hash of the log's payload, -// which is then hashed again. This is the hash that is stored in the log storage. -// The logHash can then be used to traverse from the executing message -// to the log the referenced initiating message. -// TODO(#12424): this function is duplicated between contracts and backend/source/log_processor.go -// to avoid a circular dependency. It should be reorganized to avoid this duplication. -func payloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.Hash { - msg := make([]byte, 0, 2*common.HashLength) - msg = append(msg, addr.Bytes()...) - msg = append(msg, payloadHash.Bytes()...) - return crypto.Keccak256Hash(msg) -} diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go index de5fe0eb2706..b3973c61e30b 100644 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go +++ b/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go @@ -32,7 +32,7 @@ func TestDecodeExecutingMessageEvent(t *testing.T) { Timestamp: new(big.Int).SetUint64(expected.Timestamp), LogIndex: new(big.Int).SetUint64(uint64(expected.LogIdx)), } - expected.Hash = payloadHashToLogHash(payloadHash, contractIdent.Origin) + expected.Hash = types.PayloadHashToLogHash(payloadHash, contractIdent.Origin) abi := snapshots.LoadCrossL2InboxABI() validData, err := abi.Events[eventExecutingMessage].Inputs.Pack(payloadHash, contractIdent) require.NoError(t, err) diff --git a/op-supervisor/supervisor/backend/processors/log_processor.go b/op-supervisor/supervisor/backend/processors/log_processor.go index 01846f32795c..1434d2a60395 100644 --- a/op-supervisor/supervisor/backend/processors/log_processor.go +++ b/op-supervisor/supervisor/backend/processors/log_processor.go @@ -78,7 +78,7 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts // and because they represent paired data. func logToLogHash(l *ethTypes.Log) common.Hash { payloadHash := crypto.Keccak256(logToMessagePayload(l)) - return payloadHashToLogHash(common.Hash(payloadHash), l.Address) + return types.PayloadHashToLogHash(common.Hash(payloadHash), l.Address) } // logToMessagePayload is the data that is hashed to get the logHash @@ -92,15 +92,3 @@ func logToMessagePayload(l *ethTypes.Log) []byte { msg = append(msg, l.Data...) return msg } - -// payloadHashToLogHash converts the payload hash to the log hash -// it is the concatenation of the log's address and the hash of the log's payload, -// which is then hashed. This is the hash that is stored in the log storage. -// The logHash can then be used to traverse from the executing message -// to the log the referenced initiating message. -func payloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.Hash { - msg := make([]byte, 0, 2*common.HashLength) - msg = append(msg, addr.Bytes()...) - msg = append(msg, payloadHash.Bytes()...) - return crypto.Keccak256Hash(msg) -} diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index 1c97a9119ce6..b1aab4d081e8 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -251,3 +252,15 @@ func BlockSealFromRef(ref eth.BlockRef) BlockSeal { Timestamp: ref.Time, } } + +// PayloadHashToLogHash converts the payload hash to the log hash +// it is the concatenation of the log's address and the hash of the log's payload, +// which is then hashed again. This is the hash that is stored in the log storage. +// The logHash can then be used to traverse from the executing message +// to the log the referenced initiating message. +func PayloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.Hash { + msg := make([]byte, 0, 2*common.HashLength) + msg = append(msg, addr.Bytes()...) + msg = append(msg, payloadHash.Bytes()...) + return crypto.Keccak256Hash(msg) +} From a56bfc1158cbe93a22f581952473f8c945d7c3bb Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 28 Oct 2024 21:55:33 -0700 Subject: [PATCH 071/451] proofs-tools: Update challenger version (#12719) --- docker-bake.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-bake.hcl b/docker-bake.hcl index 66a36b294bec..0da2b48aa0df 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -206,7 +206,7 @@ target "proofs-tools" { dockerfile = "./ops/docker/proofs-tools/Dockerfile" context = "." args = { - CHALLENGER_VERSION="005116d9b4e39c7ba0586ce95ceba4067123efd5" + CHALLENGER_VERSION="b46bffed42db3442d7484f089278d59f51503049" KONA_VERSION="kona-client-v0.1.0-alpha.5" } target="proofs-tools" From 36b41baea45f07e8644ff778a1ffd81fb0ad9e77 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 29 Oct 2024 16:11:22 +1000 Subject: [PATCH 072/451] cannon: Update reference for stf-verify (#12718) The prestate generation was optimised for singlethreaded-2 so update the expected baseline. --- cannon/Dockerfile.diff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cannon/Dockerfile.diff b/cannon/Dockerfile.diff index 20664a25139b..6ed17520b702 100644 --- a/cannon/Dockerfile.diff +++ b/cannon/Dockerfile.diff @@ -23,7 +23,7 @@ ARG GIT_DATE ARG TARGETOS TARGETARCH -FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.2 AS cannon-v2 +FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.3 AS cannon-v2 FROM --platform=$BUILDPLATFORM builder as cannon-verify COPY --from=cannon-v2 /usr/local/bin/cannon /usr/local/bin/cannon-v2 From bfb1ad93d674abfd9d02d715ae2701dcde867a4f Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 29 Oct 2024 22:11:00 +0700 Subject: [PATCH 073/451] op-supervisor,op-e2e: op-supervisor action test (#12691) --- op-e2e/actions/altda/altda_test.go | 4 +- op-e2e/actions/derivation/reorg_test.go | 6 +- op-e2e/actions/helpers/l2_engine.go | 17 +- op-e2e/actions/helpers/l2_engine_test.go | 6 +- op-e2e/actions/helpers/setups.go | 4 +- op-e2e/actions/interop/interop.go | 236 +++++++++++++++ op-e2e/actions/interop/interop_test.go | 270 ++++++------------ op-e2e/actions/proofs/helpers/env.go | 2 +- op-e2e/actions/upgrades/dencun_fork_test.go | 2 +- op-supervisor/supervisor/backend/backend.go | 34 +++ .../backend/processors/chain_processor.go | 2 +- 11 files changed, 373 insertions(+), 210 deletions(-) create mode 100644 op-e2e/actions/interop/interop.go diff --git a/op-e2e/actions/altda/altda_test.go b/op-e2e/actions/altda/altda_test.go index 21dcbf6b9038..3c7e06a380fc 100644 --- a/op-e2e/actions/altda/altda_test.go +++ b/op-e2e/actions/altda/altda_test.go @@ -72,7 +72,7 @@ func NewL2AltDA(t helpers.Testing, params ...AltDAParam) *L2AltDA { l1Client := miner.EthClient() jwtPath := e2eutils.WriteDefaultJWT(t) - engine := helpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + engine := helpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath) engCl := engine.EngineClient(t, sd.RollupCfg) storage := &altda.DAErrFaker{Client: altda.NewMockDAClient(log)} @@ -136,7 +136,7 @@ func (a *L2AltDA) StorageClient() *altda.DAErrFaker { func (a *L2AltDA) NewVerifier(t helpers.Testing) *helpers.L2Verifier { jwtPath := e2eutils.WriteDefaultJWT(t) - engine := helpers.NewL2Engine(t, a.log, a.sd.L2Cfg, a.sd.RollupCfg.Genesis.L1, jwtPath) + engine := helpers.NewL2Engine(t, a.log, a.sd.L2Cfg, jwtPath) engCl := engine.EngineClient(t, a.sd.RollupCfg) l1F, err := sources.NewL1Client(a.miner.RPCClient(), a.log, nil, sources.L1ClientDefaultConfig(a.sd.RollupCfg, false, sources.RPCKindBasic)) require.NoError(t, err) diff --git a/op-e2e/actions/derivation/reorg_test.go b/op-e2e/actions/derivation/reorg_test.go index 10155a471a6f..09135151d3b2 100644 --- a/op-e2e/actions/derivation/reorg_test.go +++ b/op-e2e/actions/derivation/reorg_test.go @@ -592,7 +592,7 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) require.NoError(t, err) // Sequencer - seqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, dbOption) + seqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath, dbOption) engRpc := &rpcWrapper{seqEng.RPCClient()} l2Cl, err := sources.NewEngineClient(engRpc, log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) @@ -647,7 +647,7 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { // close the sequencer engine require.NoError(t, seqEng.Close()) // and start a new one with same db path - seqEngNew := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, dbOption) + seqEngNew := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath, dbOption) // swap in the new rpc. This is as close as we can get to reconnecting to a new in-memory rpc connection engRpc.RPC = seqEngNew.RPCClient() @@ -679,7 +679,7 @@ func ConflictingL2Blocks(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { // Extra setup: a full alternative sequencer, sequencer engine, and batcher jwtPath := e2eutils.WriteDefaultJWT(t) - altSeqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + altSeqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath) altSeqEngCl, err := sources.NewEngineClient(altSeqEng.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) diff --git a/op-e2e/actions/helpers/l2_engine.go b/op-e2e/actions/helpers/l2_engine.go index 5839a20798d3..963a18769e6c 100644 --- a/op-e2e/actions/helpers/l2_engine.go +++ b/op-e2e/actions/helpers/l2_engine.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/client" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/testutils" ) @@ -37,8 +36,6 @@ type L2Engine struct { node *node.Node Eth *geth.Ethereum - rollupGenesis *rollup.Genesis - // L2 evm / chain l2Chain *core.BlockChain l2Signer types.Signer @@ -50,20 +47,14 @@ type L2Engine struct { type EngineOption func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error -func NewL2Engine(t Testing, log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.BlockID, jwtPath string, options ...EngineOption) *L2Engine { +func NewL2Engine(t Testing, log log.Logger, genesis *core.Genesis, jwtPath string, options ...EngineOption) *L2Engine { n, ethBackend, apiBackend := newBackend(t, genesis, jwtPath, options) engineApi := engineapi.NewL2EngineAPI(log, apiBackend, ethBackend.Downloader()) chain := ethBackend.BlockChain() - genesisBlock := chain.Genesis() eng := &L2Engine{ - log: log, - node: n, - Eth: ethBackend, - rollupGenesis: &rollup.Genesis{ - L1: rollupGenesisL1, - L2: eth.BlockID{Hash: genesisBlock.Hash(), Number: genesisBlock.NumberU64()}, - L2Time: genesis.Timestamp, - }, + log: log, + node: n, + Eth: ethBackend, l2Chain: chain, l2Signer: types.LatestSigner(genesis.Config), EngineApi: engineApi, diff --git a/op-e2e/actions/helpers/l2_engine_test.go b/op-e2e/actions/helpers/l2_engine_test.go index b859b393b621..115921bea94b 100644 --- a/op-e2e/actions/helpers/l2_engine_test.go +++ b/op-e2e/actions/helpers/l2_engine_test.go @@ -40,7 +40,7 @@ func TestL2EngineAPI(gt *testing.T) { tdb := triedb.NewDatabase(db, &triedb.Config{HashDB: hashdb.Defaults}) sd.L2Cfg.MustCommit(db, tdb) - engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath) l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) @@ -115,7 +115,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) { tdb := triedb.NewDatabase(db, &triedb.Config{HashDB: hashdb.Defaults}) sd.L2Cfg.MustCommit(db, tdb) - engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath) t.Cleanup(func() { _ = engine.Close() }) @@ -211,7 +211,7 @@ func TestL2EngineAPIFail(gt *testing.T) { dp := e2eutils.MakeDeployParams(t, DefaultRollupTestParams()) sd := e2eutils.Setup(t, dp, DefaultAlloc) log := testlog.Logger(t, log.LevelDebug) - engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath) // mock an RPC failure mockErr := errors.New("mock L2 RPC error") engine.ActL2RPCFail(t, mockErr) diff --git a/op-e2e/actions/helpers/setups.go b/op-e2e/actions/helpers/setups.go index 26e19eae82a0..3322dea4223e 100644 --- a/op-e2e/actions/helpers/setups.go +++ b/op-e2e/actions/helpers/setups.go @@ -25,7 +25,7 @@ func SetupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger, opts l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) require.NoError(t, err) - engine := NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, EngineWithP2P()) + engine := NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, jwtPath, EngineWithP2P()) l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) @@ -40,7 +40,7 @@ func SetupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, opt(cfg) } jwtPath := e2eutils.WriteDefaultJWT(t) - engine := NewL2Engine(t, log.New("role", "verifier-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, EngineWithP2P()) + engine := NewL2Engine(t, log.New("role", "verifier-engine"), sd.L2Cfg, jwtPath, EngineWithP2P()) engCl := engine.EngineClient(t, sd.RollupCfg) verifier := NewL2Verifier(t, log.New("role", "verifier"), l1F, blobSrc, altda.Disabled, engCl, sd.RollupCfg, syncCfg, cfg.SafeHeadListener, cfg.InteropBackend) return engine, verifier diff --git a/op-e2e/actions/interop/interop.go b/op-e2e/actions/interop/interop.go new file mode 100644 index 000000000000..4ac2b0cb80ff --- /dev/null +++ b/op-e2e/actions/interop/interop.go @@ -0,0 +1,236 @@ +package interop + +import ( + "context" + "os" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + + altda "github.com/ethereum-optimism/optimism/op-alt-da" + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" + "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/interop" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-supervisor/config" + "github.com/ethereum-optimism/optimism/op-supervisor/metrics" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +const ( + foundryArtifactsDir = "../../../packages/contracts-bedrock/forge-artifacts" + sourceMapDir = "../../../packages/contracts-bedrock" +) + +// Chain holds the most common per-chain action-test data and actors +type Chain struct { + ChainID types.ChainID + + RollupCfg *rollup.Config + ChainCfg *params.ChainConfig + BatcherAddr common.Address + + Sequencer *helpers.L2Sequencer + SequencerEngine *helpers.L2Engine + Batcher *helpers.L2Batcher +} + +// InteropSetup holds the chain deployment and config contents, before instantiating any services. +type InteropSetup struct { + Log log.Logger + Deployment *interopgen.WorldDeployment + Out *interopgen.WorldOutput + DepSet *depset.StaticConfigDependencySet + Keys devkeys.Keys + T helpers.Testing +} + +// InteropActors holds a bundle of global actors and actors of 2 chains. +type InteropActors struct { + L1Miner *helpers.L1Miner + Supervisor *SupervisorActor + ChainA *Chain + ChainB *Chain +} + +// SetupInterop creates an InteropSetup to instantiate actors on, with 2 L2 chains. +func SetupInterop(t helpers.Testing) *InteropSetup { + logger := testlog.Logger(t, log.LevelDebug) + + recipe := interopgen.InteropDevRecipe{ + L1ChainID: 900100, + L2ChainIDs: []uint64{900200, 900201}, + GenesisTimestamp: uint64(time.Now().Unix() + 3), + } + hdWallet, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + require.NoError(t, err) + worldCfg, err := recipe.Build(hdWallet) + require.NoError(t, err) + + // create the foundry artifacts and source map + foundryArtifacts := foundry.OpenArtifactsDir(foundryArtifactsDir) + sourceMap := foundry.NewSourceMapFS(os.DirFS(sourceMapDir)) + + // deploy the world, using the logger, foundry artifacts, source map, and world configuration + worldDeployment, worldOutput, err := interopgen.Deploy(logger, foundryArtifacts, sourceMap, worldCfg) + require.NoError(t, err) + + return &InteropSetup{ + Log: logger, + Deployment: worldDeployment, + Out: worldOutput, + DepSet: worldToDepSet(t, worldOutput), + Keys: hdWallet, + T: t, + } +} + +func (is *InteropSetup) CreateActors() *InteropActors { + l1Miner := helpers.NewL1Miner(is.T, is.Log.New("role", "l1Miner"), is.Out.L1.Genesis) + supervisorAPI := NewSupervisor(is.T, is.Log, is.DepSet) + require.NoError(is.T, supervisorAPI.Start(is.T.Ctx())) + is.T.Cleanup(func() { + require.NoError(is.T, supervisorAPI.Stop(context.Background())) + }) + chainA := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900200"], supervisorAPI) + chainB := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900201"], supervisorAPI) + // Hook up L2 RPCs to supervisor, to fetch event data from + require.NoError(is.T, supervisorAPI.AddL2RPC(is.T.Ctx(), chainA.SequencerEngine.HTTPEndpoint())) + require.NoError(is.T, supervisorAPI.AddL2RPC(is.T.Ctx(), chainB.SequencerEngine.HTTPEndpoint())) + return &InteropActors{ + L1Miner: l1Miner, + Supervisor: supervisorAPI, + ChainA: chainA, + ChainB: chainB, + } +} + +// SupervisorActor represents a supervisor, instrumented to run synchronously for action-test purposes. +type SupervisorActor struct { + backend *backend.SupervisorBackend + frontend.QueryFrontend + frontend.AdminFrontend + frontend.UpdatesFrontend +} + +func (sa *SupervisorActor) SyncEvents(t helpers.Testing, chainID types.ChainID) { + require.NoError(t, sa.backend.SyncEvents(chainID)) +} + +func (sa *SupervisorActor) SyncCrossUnsafe(t helpers.Testing, chainID types.ChainID) { + require.NoError(t, sa.backend.SyncCrossUnsafe(chainID)) +} + +func (sa *SupervisorActor) SyncCrossSafe(t helpers.Testing, chainID types.ChainID) { + require.NoError(t, sa.backend.SyncCrossSafe(chainID)) +} + +// worldToDepSet converts a set of chain configs into a dependency-set for the supervisor. +func worldToDepSet(t helpers.Testing, worldOutput *interopgen.WorldOutput) *depset.StaticConfigDependencySet { + depSetCfg := make(map[types.ChainID]*depset.StaticConfigDependency) + for _, out := range worldOutput.L2s { + depSetCfg[types.ChainIDFromBig(out.Genesis.Config.ChainID)] = &depset.StaticConfigDependency{ + ChainIndex: types.ChainIndex(out.Genesis.Config.ChainID.Uint64()), + ActivationTime: 0, + HistoryMinTime: 0, + } + } + depSet, err := depset.NewStaticConfigDependencySet(depSetCfg) + require.NoError(t, err) + return depSet +} + +// NewSupervisor creates a new SupervisorActor, to action-test the supervisor with. +func NewSupervisor(t helpers.Testing, logger log.Logger, depSet depset.DependencySetSource) *SupervisorActor { + logger = logger.New("role", "supervisor") + supervisorDataDir := t.TempDir() + logger.Info("supervisor data dir", "dir", supervisorDataDir) + svCfg := &config.Config{ + DependencySetSource: depSet, + SynchronousProcessors: true, + Datadir: supervisorDataDir, + } + b, err := backend.NewSupervisorBackend(t.Ctx(), + logger.New("role", "supervisor"), metrics.NoopMetrics, svCfg) + require.NoError(t, err) + return &SupervisorActor{ + backend: b, + QueryFrontend: frontend.QueryFrontend{ + Supervisor: b, + }, + AdminFrontend: frontend.AdminFrontend{ + Supervisor: b, + }, + UpdatesFrontend: frontend.UpdatesFrontend{ + Supervisor: b, + }, + } +} + +// createL2Services creates a Chain bundle, with the given configs, and attached to the given L1 miner. +func createL2Services( + t helpers.Testing, + logger log.Logger, + l1Miner *helpers.L1Miner, + keys devkeys.Keys, + output *interopgen.L2Output, + interopBackend interop.InteropBackend, +) *Chain { + logger = logger.New("chain", output.Genesis.Config.ChainID) + + jwtPath := e2eutils.WriteDefaultJWT(t) + + eng := helpers.NewL2Engine(t, logger.New("role", "engine"), output.Genesis, jwtPath) + + seqCl, err := sources.NewEngineClient(eng.RPCClient(), logger, nil, sources.EngineClientDefaultConfig(output.RollupCfg)) + require.NoError(t, err) + + l1F, err := sources.NewL1Client(l1Miner.RPCClient(), logger, nil, + sources.L1ClientDefaultConfig(output.RollupCfg, false, sources.RPCKindStandard)) + require.NoError(t, err) + + seq := helpers.NewL2Sequencer(t, logger.New("role", "sequencer"), l1F, + l1Miner.BlobStore(), altda.Disabled, seqCl, output.RollupCfg, + 0, interopBackend) + + batcherKey, err := keys.Secret(devkeys.ChainOperatorKey{ + ChainID: output.Genesis.Config.ChainID, + Role: devkeys.BatcherRole, + }) + require.NoError(t, err) + + batcherCfg := &helpers.BatcherCfg{ + MinL1TxSize: 0, + MaxL1TxSize: 128_000, + BatcherKey: batcherKey, + DataAvailabilityType: batcherFlags.CalldataType, + } + + batcher := helpers.NewL2Batcher(logger.New("role", "batcher"), output.RollupCfg, batcherCfg, + seq.RollupClient(), l1Miner.EthClient(), + eng.EthClient(), eng.EngineClient(t, output.RollupCfg)) + + return &Chain{ + ChainID: types.ChainIDFromBig(output.Genesis.Config.ChainID), + RollupCfg: output.RollupCfg, + ChainCfg: output.Genesis.Config, + BatcherAddr: crypto.PubkeyToAddress(batcherKey.PublicKey), + Sequencer: seq, + SequencerEngine: eng, + Batcher: batcher, + } +} diff --git a/op-e2e/actions/interop/interop_test.go b/op-e2e/actions/interop/interop_test.go index a69a09a9bd94..a39da702db8c 100644 --- a/op-e2e/actions/interop/interop_test.go +++ b/op-e2e/actions/interop/interop_test.go @@ -1,207 +1,109 @@ package interop import ( - "context" "testing" "github.com/stretchr/testify/require" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" - "github.com/ethereum-optimism/optimism/op-node/rollup/interop" - "github.com/ethereum-optimism/optimism/op-node/rollup/sync" - "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum-optimism/optimism/op-service/testutils" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) -var _ interop.InteropBackend = (*testutils.FakeInteropBackend)(nil) - -func TestInteropVerifier(gt *testing.T) { +func TestFullInterop(gt *testing.T) { t := helpers.NewDefaultTesting(gt) - dp := e2eutils.MakeDeployParams(t, helpers.DefaultRollupTestParams()) - sd := e2eutils.Setup(t, dp, helpers.DefaultAlloc) - // Temporary work-around: interop needs to be active, for cross-safety to not be instant. - // The state genesis in this test is pre-interop however. - sd.RollupCfg.InteropTime = new(uint64) - logger := testlog.Logger(t, log.LevelDebug) - seqMockBackend := &testutils.FakeInteropBackend{} - l1Miner, seqEng, seq := helpers.SetupSequencerTest(t, sd, logger, - helpers.WithVerifierOpts(helpers.WithInteropBackend(seqMockBackend))) - - batcher := helpers.NewL2Batcher(logger, sd.RollupCfg, helpers.DefaultBatcherCfg(dp), - seq.RollupClient(), l1Miner.EthClient(), seqEng.EthClient(), seqEng.EngineClient(t, sd.RollupCfg)) - - verMockBackend := &testutils.FakeInteropBackend{} - _, ver := helpers.SetupVerifier(t, sd, logger, - l1Miner.L1Client(t, sd.RollupCfg), l1Miner.BlobStore(), &sync.Config{}, - helpers.WithInteropBackend(verMockBackend)) - // Genesis block will be registered as local-safe when we first trigger the derivation pipeline - seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { - require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) - require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) - return nil - } - verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { - require.Equal(t, sd.RollupCfg.Genesis.L1, derivedFrom.ID()) - require.Equal(t, sd.RollupCfg.Genesis.L2, lastDerived.ID()) - return nil - } - - seq.ActL2PipelineFull(t) - ver.ActL2PipelineFull(t) - - l2ChainID := types.ChainIDFromBig(sd.RollupCfg.L2ChainID) - seqMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { - require.Equal(t, chainID, l2ChainID) - require.Equal(t, uint64(1), head.Number) - return nil - } - seqMockBackend.UnsafeViewFn = func(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, chainID, l2ChainID) - require.Equal(t, uint64(1), unsafe.Local.Number) - require.Equal(t, uint64(0), unsafe.Cross.Number) - return unsafe, nil - } - // create an unsafe L2 block - seq.ActL2StartBlock(t) - seq.ActL2EndBlock(t) - seq.ActL2PipelineFull(t) - status := seq.SyncStatus() - require.Equal(t, uint64(1), status.UnsafeL2.Number) + is := SetupInterop(t) + actors := is.CreateActors() + + // get both sequencers set up + actors.ChainA.Sequencer.ActL2PipelineFull(t) + actors.ChainB.Sequencer.ActL2PipelineFull(t) + + // No blocks yet + status := actors.ChainA.Sequencer.SyncStatus() + require.Equal(t, uint64(0), status.UnsafeL2.Number) + + // sync chain A + actors.Supervisor.SyncEvents(t, actors.ChainA.ChainID) + actors.Supervisor.SyncCrossUnsafe(t, actors.ChainA.ChainID) + actors.Supervisor.SyncCrossSafe(t, actors.ChainA.ChainID) + + // sync chain B + actors.Supervisor.SyncEvents(t, actors.ChainB.ChainID) + actors.Supervisor.SyncCrossUnsafe(t, actors.ChainB.ChainID) + actors.Supervisor.SyncCrossSafe(t, actors.ChainB.ChainID) + + // Build L2 block on chain A + actors.ChainA.Sequencer.ActL2StartBlock(t) + actors.ChainA.Sequencer.ActL2EndBlock(t) + status = actors.ChainA.Sequencer.SyncStatus() + head := status.UnsafeL2.ID() + require.Equal(t, uint64(1), head.Number) require.Equal(t, uint64(0), status.CrossUnsafeL2.Number) require.Equal(t, uint64(0), status.LocalSafeL2.Number) require.Equal(t, uint64(0), status.SafeL2.Number) + require.Equal(t, uint64(0), status.FinalizedL2.Number) - // promote it to cross-unsafe in the backend - // and see if the node picks up on it - seqMockBackend.UnsafeViewFn = func(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, chainID, l2ChainID) - require.Equal(t, uint64(1), unsafe.Local.Number) - require.Equal(t, uint64(0), unsafe.Cross.Number) - out := unsafe - out.Cross = unsafe.Local - return out, nil - } - seq.ActInteropBackendCheck(t) - seq.ActL2PipelineFull(t) - status = seq.SyncStatus() - require.Equal(t, uint64(1), status.UnsafeL2.Number) - require.Equal(t, uint64(1), status.CrossUnsafeL2.Number, "cross unsafe now") + // Verify as cross-unsafe with supervisor + actors.Supervisor.SyncEvents(t, actors.ChainA.ChainID) + actors.Supervisor.SyncCrossUnsafe(t, actors.ChainA.ChainID) + actors.ChainA.Sequencer.ActL2PipelineFull(t) + status = actors.ChainA.Sequencer.SyncStatus() + require.Equal(t, head, status.UnsafeL2.ID()) + require.Equal(t, head, status.CrossUnsafeL2.ID()) require.Equal(t, uint64(0), status.LocalSafeL2.Number) require.Equal(t, uint64(0), status.SafeL2.Number) + require.Equal(t, uint64(0), status.FinalizedL2.Number) - // submit all new L2 blocks - batcher.ActSubmitAll(t) - // new L1 block with L2 batch - l1Miner.ActL1StartBlock(12)(t) - l1Miner.ActL1IncludeTx(sd.RollupCfg.Genesis.SystemConfig.BatcherAddr)(t) - l1Miner.ActL1EndBlock(t) - - // Sync the L1 block, to verify the L2 block as local-safe. - seqMockBackend.UpdateLocalUnsafeFn = nil - nextL2 := uint64(0) - seqMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { - require.Equal(t, nextL2, lastDerived.Number) - nextL2 += 1 - return nil - } - seqMockBackend.SafeViewFn = func(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, chainID, l2ChainID) - require.Equal(t, uint64(1), safe.Local.Number) - require.Equal(t, uint64(0), safe.Cross.Number) - return safe, nil - } - seq.ActL1HeadSignal(t) - l1Head := seq.SyncStatus().HeadL1 - seq.ActL2PipelineFull(t) - require.Equal(t, uint64(2), nextL2) - - status = seq.SyncStatus() - require.Equal(t, uint64(1), status.UnsafeL2.Number) - require.Equal(t, uint64(1), status.CrossUnsafeL2.Number) - require.Equal(t, uint64(1), status.LocalSafeL2.Number, "local safe changed") + // Submit the L2 block, sync the local-safe data + actors.ChainA.Batcher.ActSubmitAll(t) + actors.L1Miner.ActL1StartBlock(12)(t) + actors.L1Miner.ActL1IncludeTx(actors.ChainA.BatcherAddr)(t) + actors.L1Miner.ActL1EndBlock(t) + actors.ChainA.Sequencer.ActL1HeadSignal(t) + actors.ChainA.Sequencer.ActL2PipelineFull(t) + status = actors.ChainA.Sequencer.SyncStatus() + require.Equal(t, head, status.UnsafeL2.ID()) + require.Equal(t, head, status.CrossUnsafeL2.ID()) + require.Equal(t, head, status.LocalSafeL2.ID()) require.Equal(t, uint64(0), status.SafeL2.Number) - - // Now mark it as cross-safe - seqMockBackend.SafeViewFn = func(ctx context.Context, chainID types.ChainID, request types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, chainID, l2ChainID) - require.Equal(t, uint64(1), request.Local.Number) - require.Equal(t, uint64(0), request.Cross.Number) - out := request - out.Cross = request.Local - return out, nil - } - seqMockBackend.DerivedFromFn = func(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { - require.Equal(t, uint64(1), derived.Number) - return l1Head, nil - } - seqMockBackend.FinalizedFn = func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { - return seq.RollupCfg.Genesis.L1, nil - } - seq.ActInteropBackendCheck(t) - seq.ActL2PipelineFull(t) - - status = seq.SyncStatus() - require.Equal(t, uint64(1), status.UnsafeL2.Number) - require.Equal(t, uint64(1), status.CrossUnsafeL2.Number) - require.Equal(t, uint64(1), status.LocalSafeL2.Number) - require.Equal(t, uint64(1), status.SafeL2.Number, "cross-safe reached") require.Equal(t, uint64(0), status.FinalizedL2.Number) - - verMockBackend.UpdateLocalUnsafeFn = func(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { - require.Equal(t, uint64(1), head.Number) - return nil - } - nextL2 = 0 - verMockBackend.UpdateLocalSafeFn = func(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error { - require.Equal(t, nextL2, lastDerived.Number) - nextL2 += 1 - require.Equal(t, l1Head.ID(), derivedFrom.ID()) - return nil - } - // The verifier might not see the L2 block that was just derived from L1 as cross-verified yet. - verMockBackend.UnsafeViewFn = func(ctx context.Context, chainID types.ChainID, request types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, uint64(1), request.Local.Number) - require.Equal(t, uint64(0), request.Cross.Number) - // Don't promote the Cross value yet - return request, nil - } - verMockBackend.SafeViewFn = func(ctx context.Context, chainID types.ChainID, request types.ReferenceView) (types.ReferenceView, error) { - require.Equal(t, uint64(1), request.Local.Number) - require.Equal(t, uint64(0), request.Cross.Number) - // Don't promote the Cross value yet - return request, nil - } - ver.ActL1HeadSignal(t) - ver.ActL2PipelineFull(t) - require.Equal(t, uint64(2), nextL2) - status = ver.SyncStatus() - require.Equal(t, uint64(1), status.UnsafeL2.Number, "synced the block") - require.Equal(t, uint64(0), status.CrossUnsafeL2.Number, "not cross-verified yet") - require.Equal(t, uint64(1), status.LocalSafeL2.Number, "derived from L1, thus local-safe") - require.Equal(t, uint64(0), status.SafeL2.Number, "not yet cross-safe") + // Local-safe does not count as "safe" in RPC + n := actors.ChainA.SequencerEngine.L2Chain().CurrentSafeBlock().Number.Uint64() + require.Equal(t, uint64(0), n) + + // Cross-safe verify it + actors.Supervisor.SyncCrossSafe(t, actors.ChainA.ChainID) + actors.ChainA.Sequencer.ActInteropBackendCheck(t) + actors.ChainA.Sequencer.ActL2PipelineFull(t) + status = actors.ChainA.Sequencer.SyncStatus() + require.Equal(t, head, status.UnsafeL2.ID()) + require.Equal(t, head, status.CrossUnsafeL2.ID()) + require.Equal(t, head, status.LocalSafeL2.ID()) + require.Equal(t, head, status.SafeL2.ID()) require.Equal(t, uint64(0), status.FinalizedL2.Number) - - seqMockBackend.UpdateFinalizedL1Fn = func(ctx context.Context, chainID types.ChainID, finalized eth.L1BlockRef) error { - require.Equal(t, l1Head, finalized) - return nil - } - // Allow the supervisor to promote the cross-safe L2 block to finalized. - seqMockBackend.FinalizedFn = func(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { - return seq.SyncStatus().SafeL2.ID(), nil - } - // signal that L1 finalized; the cross-safe block we have should get finalized too - l1Miner.ActL1SafeNext(t) - l1Miner.ActL1FinalizeNext(t) - seq.ActL1SafeSignal(t) - seq.ActL1FinalizedSignal(t) - seq.ActL2PipelineFull(t) - - status = seq.SyncStatus() - require.Equal(t, uint64(1), status.FinalizedL2.Number, "finalized the block") + h := actors.ChainA.SequencerEngine.L2Chain().CurrentSafeBlock().Hash() + require.Equal(t, head.Hash, h) + + // Finalize L1, and see how the op-node forwards it to the supervisor. + // The supervisor then determines finality, which the op-node can use. + actors.L1Miner.ActL1SafeNext(t) + actors.L1Miner.ActL1FinalizeNext(t) + actors.ChainA.Sequencer.ActL1SafeSignal(t) + actors.ChainA.Sequencer.ActL1FinalizedSignal(t) + actors.ChainA.Sequencer.ActL2PipelineFull(t) + finalizedL2BlockID, err := actors.Supervisor.Finalized(t.Ctx(), actors.ChainA.ChainID) + require.NoError(t, err) + require.Equal(t, head, finalizedL2BlockID) + + // The op-node needs a poke to look at the updated supervisor finality state + actors.ChainA.Sequencer.ActInteropBackendCheck(t) + actors.ChainA.Sequencer.ActL2PipelineFull(t) + h = actors.ChainA.SequencerEngine.L2Chain().CurrentFinalBlock().Hash() + require.Equal(t, head.Hash, h) + status = actors.ChainA.Sequencer.SyncStatus() + require.Equal(t, head, status.UnsafeL2.ID()) + require.Equal(t, head, status.CrossUnsafeL2.ID()) + require.Equal(t, head, status.LocalSafeL2.ID()) + require.Equal(t, head, status.SafeL2.ID()) + require.Equal(t, head, status.FinalizedL2.ID()) } diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 75b7f2554f4c..45e8b4d7e346 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -74,7 +74,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut l1Cl, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) require.NoError(t, err) - engine := helpers.NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, helpers.EngineWithP2P()) + engine := helpers.NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, jwtPath, helpers.EngineWithP2P()) l2EngineCl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) diff --git a/op-e2e/actions/upgrades/dencun_fork_test.go b/op-e2e/actions/upgrades/dencun_fork_test.go index adc995c0636d..60f59abedcc6 100644 --- a/op-e2e/actions/upgrades/dencun_fork_test.go +++ b/op-e2e/actions/upgrades/dencun_fork_test.go @@ -190,7 +190,7 @@ func aliceSimpleBlobTx(t helpers.Testing, dp *e2eutils.DeployParams) *types.Tran func newEngine(t helpers.Testing, sd *e2eutils.SetupData, log log.Logger) *helpers.L2Engine { jwtPath := e2eutils.WriteDefaultJWT(t) - return helpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) + return helpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath) } // TestDencunBlobTxRPC tries to send a Blob tx to the L2 engine via RPC, it should not be accepted. diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index a9322b84c057..ff5d5575cd31 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -439,3 +439,37 @@ func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID type return su.chainDBs.UpdateFinalizedL1(finalized) } + +// Access to synchronous processing for tests +// ---------------------------- + +func (su *SupervisorBackend) SyncEvents(chainID types.ChainID) error { + su.mu.RLock() + defer su.mu.RUnlock() + ch, ok := su.chainProcessors[chainID] + if !ok { + return types.ErrUnknownChain + } + ch.ProcessToHead() + return nil +} + +func (su *SupervisorBackend) SyncCrossUnsafe(chainID types.ChainID) error { + su.mu.RLock() + defer su.mu.RUnlock() + ch, ok := su.crossUnsafeProcessors[chainID] + if !ok { + return types.ErrUnknownChain + } + return ch.ProcessWork() +} + +func (su *SupervisorBackend) SyncCrossSafe(chainID types.ChainID) error { + su.mu.RLock() + defer su.mu.RUnlock() + ch, ok := su.crossSafeProcessors[chainID] + if !ok { + return types.ErrUnknownChain + } + return ch.ProcessWork() +} diff --git a/op-supervisor/supervisor/backend/processors/chain_processor.go b/op-supervisor/supervisor/backend/processors/chain_processor.go index db57e977aae0..7f4687086030 100644 --- a/op-supervisor/supervisor/backend/processors/chain_processor.go +++ b/op-supervisor/supervisor/backend/processors/chain_processor.go @@ -134,7 +134,7 @@ func (s *ChainProcessor) work() { target := s.nextNum() if err := s.update(target); err != nil { if errors.Is(err, ethereum.NotFound) { - s.log.Info("Cannot find next block yet", "target", target) + s.log.Info("Cannot find next block yet", "target", target, "err", err) } else if errors.Is(err, types.ErrNoRPCSource) { s.log.Warn("No RPC source configured, cannot process new blocks") } else { From 13d1fc17a568fb43ac4fce1024e2c6d4896a3583 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Wed, 30 Oct 2024 01:27:07 +1000 Subject: [PATCH 074/451] proofs-tools: Update kona to 0.1.0-alpha.6 (#12717) Co-authored-by: clabby --- docker-bake.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-bake.hcl b/docker-bake.hcl index 0da2b48aa0df..4f72678a8cab 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -207,7 +207,7 @@ target "proofs-tools" { context = "." args = { CHALLENGER_VERSION="b46bffed42db3442d7484f089278d59f51503049" - KONA_VERSION="kona-client-v0.1.0-alpha.5" + KONA_VERSION="kona-client-v0.1.0-alpha.6" } target="proofs-tools" platforms = split(",", PLATFORMS) From 4b1c12ad4312fe6fe28d8297a26cccf053356704 Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Tue, 29 Oct 2024 23:20:17 +0700 Subject: [PATCH 075/451] Cross safe updates use cycle checks (#12726) * tweak: Make cycle checks work with an OpenBlock that returns BlockRef. * tests,fix: Make tests generate correct hazard maps. * tests: Add assertion on returned blockRef. * tests,fix: Fix TestCrossUnsafeUpdate to use correct mock log count. * tweak: Call HazardCycleChecks from scopedCrossSafeUpdate and CrossUnsafeUpdate. * tests,cleanup: Fix test assertion comment. * tests,fix: Fix TestCrossSafeUpdate. --- .../supervisor/backend/cross/cycle.go | 10 ++++- .../supervisor/backend/cross/cycle_test.go | 23 +++++----- .../supervisor/backend/cross/safe_update.go | 6 +-- .../backend/cross/safe_update_test.go | 45 ++++++++++++++++++- .../supervisor/backend/cross/unsafe_update.go | 6 +-- .../backend/cross/unsafe_update_test.go | 31 ++++++++++++- 6 files changed, 100 insertions(+), 21 deletions(-) diff --git a/op-supervisor/supervisor/backend/cross/cycle.go b/op-supervisor/supervisor/backend/cross/cycle.go index a872bc75a860..9884856a34a5 100644 --- a/op-supervisor/supervisor/backend/cross/cycle.go +++ b/op-supervisor/supervisor/backend/cross/cycle.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -17,7 +18,7 @@ var ( // CycleCheckDeps is an interface for checking cyclical dependencies between logs. type CycleCheckDeps interface { // OpenBlock returns log data for the requested block, to be used for cycle checking. - OpenBlock(chainID types.ChainID, blockNum uint64) (seal types.BlockSeal, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) + OpenBlock(chainID types.ChainID, blockNum uint64) (block eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) } // node represents a log entry in the dependency graph. @@ -95,7 +96,8 @@ func gatherLogs(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainInd if err != nil { return nil, nil, fmt.Errorf("failed to open block: %w", err) } - if bl != hazardBlock { + + if !blockSealMatchesRef(hazardBlock, bl) { return nil, nil, fmt.Errorf("tried to open block %s of chain %s, but got different block %s than expected, use a reorg lock for consistency", hazardBlock, hazardChainID, bl) } @@ -246,6 +248,10 @@ func checkGraph(g *graph) error { } } +func blockSealMatchesRef(seal types.BlockSeal, ref eth.BlockRef) bool { + return seal.Number == ref.Number && seal.Hash == ref.Hash +} + // GenerateMermaidDiagram creates a Mermaid flowchart diagram from the graph data for debugging. func GenerateMermaidDiagram(g *graph) string { var sb strings.Builder diff --git a/op-supervisor/supervisor/backend/cross/cycle_test.go b/op-supervisor/supervisor/backend/cross/cycle_test.go index a90258b88a26..e160023caf81 100644 --- a/op-supervisor/supervisor/backend/cross/cycle_test.go +++ b/op-supervisor/supervisor/backend/cross/cycle_test.go @@ -8,14 +8,15 @@ import ( "github.com/stretchr/testify/require" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) type mockCycleCheckDeps struct { - openBlockFn func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) + openBlockFn func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) } -func (m *mockCycleCheckDeps) OpenBlock(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { +func (m *mockCycleCheckDeps) OpenBlock(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) { return m.openBlockFn(chainID, blockNum) } @@ -33,7 +34,7 @@ type hazardCycleChecksTestCase struct { // Optional overrides hazards map[types.ChainIndex]types.BlockSeal - openBlockFn func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) + openBlockFn func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) } func runHazardCycleChecksTestCaseGroup(t *testing.T, group string, tests []hazardCycleChecksTestCase) { @@ -47,7 +48,7 @@ func runHazardCycleChecksTestCaseGroup(t *testing.T, group string, tests []hazar func runHazardCycleChecksTestCase(t *testing.T, tc hazardCycleChecksTestCase) { // Create mocked dependencies deps := &mockCycleCheckDeps{ - openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { + openBlockFn: func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) { // Use override if provided if tc.openBlockFn != nil { return tc.openBlockFn(chainID, blockNum) @@ -57,12 +58,12 @@ func runHazardCycleChecksTestCase(t *testing.T, tc hazardCycleChecksTestCase) { chainStr := chainID.String() def, ok := tc.chainBlocks[chainStr] if !ok { - return types.BlockSeal{}, 0, nil, errors.New("unexpected chain") + return eth.BlockRef{}, 0, nil, errors.New("unexpected chain") } if def.error != nil { - return types.BlockSeal{}, 0, nil, def.error + return eth.BlockRef{}, 0, nil, def.error } - return types.BlockSeal{Number: blockNum}, def.logCount, def.messages, nil + return eth.BlockRef{Number: blockNum}, def.logCount, def.messages, nil }, } @@ -149,8 +150,8 @@ func TestHazardCycleChecksFailures(t *testing.T) { { name: "failed to open block error", chainBlocks: emptyChainBlocks, - openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { - return types.BlockSeal{}, 0, nil, testOpenBlockErr + openBlockFn: func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) { + return eth.BlockRef{}, 0, nil, testOpenBlockErr }, expectErr: errors.New("failed to open block"), msg: "expected error when OpenBlock fails", @@ -159,8 +160,8 @@ func TestHazardCycleChecksFailures(t *testing.T) { name: "block mismatch error", chainBlocks: emptyChainBlocks, // openBlockFn returns a block number that doesn't match the expected block number. - openBlockFn: func(chainID types.ChainID, blockNum uint64) (types.BlockSeal, uint32, map[uint32]*types.ExecutingMessage, error) { - return types.BlockSeal{Number: blockNum + 1}, 0, make(map[uint32]*types.ExecutingMessage), nil + openBlockFn: func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error) { + return eth.BlockRef{Number: blockNum + 1}, 0, make(map[uint32]*types.ExecutingMessage), nil }, expectErr: errors.New("tried to open block"), msg: "expected error due to block mismatch", diff --git a/op-supervisor/supervisor/backend/cross/safe_update.go b/op-supervisor/supervisor/backend/cross/safe_update.go index 172277db52de..6d74ca8ad2ba 100644 --- a/op-supervisor/supervisor/backend/cross/safe_update.go +++ b/op-supervisor/supervisor/backend/cross/safe_update.go @@ -89,9 +89,9 @@ func scopedCrossSafeUpdate(logger log.Logger, chainID types.ChainID, d CrossSafe if err := HazardSafeFrontierChecks(d, candidateScope.ID(), hazards); err != nil { return candidateScope, fmt.Errorf("failed to verify block %s in cross-safe frontier: %w", candidate, err) } - //if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil { - // TODO - //} + if err := HazardCycleChecks(d, candidate.Time, hazards); err != nil { + return candidateScope, fmt.Errorf("failed to verify block %s in cross-safe check for cycle hazards: %w", candidate, err) + } // promote the candidate block to cross-safe if err := d.UpdateCrossSafe(chainID, candidateScope, candidate); err != nil { diff --git a/op-supervisor/supervisor/backend/cross/safe_update_test.go b/op-supervisor/supervisor/backend/cross/safe_update_test.go index fec5f69b8ca4..b3361c711bd0 100644 --- a/op-supervisor/supervisor/backend/cross/safe_update_test.go +++ b/op-supervisor/supervisor/backend/cross/safe_update_test.go @@ -30,6 +30,9 @@ func TestCrossSafeUpdate(t *testing.T) { csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { return opened, 10, execs, nil } + csd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } csd.deps = mockDependencySet{} // when scopedCrossSafeUpdate returns no error, // no error is returned @@ -256,6 +259,9 @@ func TestScopedCrossSafeUpdate(t *testing.T) { csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { return opened, 10, execs, nil } + csd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } count := 0 csd.deps = mockDependencySet{} // cause CrossSafeHazards to return an error by making ChainIDFromIndex return an error @@ -274,6 +280,32 @@ func TestScopedCrossSafeUpdate(t *testing.T) { require.ErrorContains(t, err, "frontier") require.Equal(t, eth.BlockRef{}, blockRef) }) + t.Run("HazardCycleChecks returns error", func(t *testing.T) { + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + csd := &mockCrossSafeDeps{} + candidate := eth.BlockRef{Number: 1, Time: 1} + candidateScope := eth.BlockRef{Number: 2} + csd.candidateCrossSafeFn = func() (derivedFromScope, crossSafe eth.BlockRef, err error) { + return candidateScope, candidate, nil + } + opened := eth.BlockRef{Number: 1, Time: 1} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1, LogIdx: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1, LogIdx: 1} + csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return opened, 3, map[uint32]*types.ExecutingMessage{1: em1, 2: em2}, nil + } + csd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } + csd.deps = mockDependencySet{} + + // HazardCycleChecks returns an error with appropriate wrapping + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) + require.ErrorContains(t, err, "cycle detected") + require.ErrorContains(t, err, "failed to verify block") + require.Equal(t, eth.BlockRef{Number: 2}, blockRef) + }) t.Run("UpdateCrossSafe returns error", func(t *testing.T) { logger := testlog.Logger(t, log.LevelDebug) chainID := types.ChainIDFromUInt64(0) @@ -288,15 +320,19 @@ func TestScopedCrossSafeUpdate(t *testing.T) { csd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { return opened, 10, execs, nil } + csd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } csd.deps = mockDependencySet{} csd.updateCrossSafeFn = func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { return errors.New("some error") } // when UpdateCrossSafe returns an error, // the error is returned - _, err := scopedCrossSafeUpdate(logger, chainID, csd) + blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) require.ErrorContains(t, err, "some error") require.ErrorContains(t, err, "failed to update") + require.Equal(t, eth.BlockRef{Number: 2}, blockRef) }) t.Run("successful update", func(t *testing.T) { logger := testlog.Logger(t, log.LevelDebug) @@ -325,6 +361,9 @@ func TestScopedCrossSafeUpdate(t *testing.T) { // when no errors occur, the update is carried out // the used candidate and scope are from CandidateCrossSafe // the candidateScope is returned + csd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } blockRef, err := scopedCrossSafeUpdate(logger, chainID, csd) require.Equal(t, chainID, updatingChain) require.Equal(t, candidateScope, updatingCandidateScope) @@ -342,6 +381,7 @@ type mockCrossSafeDeps struct { updateCrossSafeFn func(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error nextDerivedFromFn func(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) previousDerivedFn func(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) + checkFn func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) } func (m *mockCrossSafeDeps) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { @@ -367,6 +407,9 @@ func (m *mockCrossSafeDeps) CrossDerivedFrom(chainID types.ChainID, derived eth. } func (m *mockCrossSafeDeps) Check(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + if m.checkFn != nil { + return m.checkFn(chainID, blockNum, logIdx, logHash) + } return types.BlockSeal{}, nil } diff --git a/op-supervisor/supervisor/backend/cross/unsafe_update.go b/op-supervisor/supervisor/backend/cross/unsafe_update.go index bc13146d38ff..56d396127158 100644 --- a/op-supervisor/supervisor/backend/cross/unsafe_update.go +++ b/op-supervisor/supervisor/backend/cross/unsafe_update.go @@ -60,9 +60,9 @@ func CrossUnsafeUpdate(ctx context.Context, logger log.Logger, chainID types.Cha if err := HazardUnsafeFrontierChecks(d, hazards); err != nil { return fmt.Errorf("failed to verify block %s in cross-unsafe frontier: %w", candidate, err) } - //if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil { - //// TODO - //} + if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil { + return fmt.Errorf("failed to verify block %s in cross-unsafe check for cycle hazards: %w", candidate, err) + } // promote the candidate block to cross-unsafe if err := d.UpdateCrossUnsafe(chainID, candidate); err != nil { diff --git a/op-supervisor/supervisor/backend/cross/unsafe_update_test.go b/op-supervisor/supervisor/backend/cross/unsafe_update_test.go index c0e264c97206..ada7d0756cf4 100644 --- a/op-supervisor/supervisor/backend/cross/unsafe_update_test.go +++ b/op-supervisor/supervisor/backend/cross/unsafe_update_test.go @@ -131,6 +131,31 @@ func TestCrossUnsafeUpdate(t *testing.T) { err := CrossUnsafeUpdate(ctx, logger, chainID, usd) require.ErrorContains(t, err, "some error") }) + t.Run("HazardCycleChecks returns error", func(t *testing.T) { + ctx := context.Background() + logger := testlog.Logger(t, log.LevelDebug) + chainID := types.ChainIDFromUInt64(0) + usd := &mockCrossUnsafeDeps{} + crossUnsafe := types.BlockSeal{Hash: common.Hash{0x01}} + usd.crossUnsafeFn = func(chainID types.ChainID) (types.BlockSeal, error) { + return crossUnsafe, nil + } + bl := eth.BlockRef{ParentHash: common.Hash{0x01}, Number: 1, Time: 1} + em1 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1, LogIdx: 2} + em2 := &types.ExecutingMessage{Chain: types.ChainIndex(0), Timestamp: 1, LogIdx: 1} + usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { + return bl, 3, map[uint32]*types.ExecutingMessage{1: em1, 2: em2}, nil + } + usd.checkFn = func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + return types.BlockSeal{Number: 1, Timestamp: 1}, nil + } + usd.deps = mockDependencySet{} + + // HazardCycleChecks returns an error with appropriate wrapping + err := CrossUnsafeUpdate(ctx, logger, chainID, usd) + require.ErrorContains(t, err, "cycle detected") + require.ErrorContains(t, err, "failed to verify block") + }) t.Run("successful update", func(t *testing.T) { ctx := context.Background() logger := testlog.Logger(t, log.LevelDebug) @@ -144,7 +169,7 @@ func TestCrossUnsafeUpdate(t *testing.T) { em1 := &types.ExecutingMessage{Timestamp: 1} usd.openBlockFn = func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { // include one executing message to ensure one hazard is returned - return bl, 0, map[uint32]*types.ExecutingMessage{1: em1}, nil + return bl, 2, map[uint32]*types.ExecutingMessage{1: em1}, nil } usd.deps = mockDependencySet{} var updatingChainID types.ChainID @@ -168,6 +193,7 @@ type mockCrossUnsafeDeps struct { crossUnsafeFn func(chainID types.ChainID) (types.BlockSeal, error) openBlockFn func(chainID types.ChainID, blockNum uint64) (ref eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) updateCrossUnsafeFn func(chain types.ChainID, crossUnsafe types.BlockSeal) error + checkFn func(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) } func (m *mockCrossUnsafeDeps) CrossUnsafe(chainID types.ChainID) (derived types.BlockSeal, err error) { @@ -182,6 +208,9 @@ func (m *mockCrossUnsafeDeps) DependencySet() depset.DependencySet { } func (m *mockCrossUnsafeDeps) Check(chainID types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (types.BlockSeal, error) { + if m.checkFn != nil { + return m.checkFn(chainID, blockNum, logIdx, logHash) + } return types.BlockSeal{}, nil } From 2780ad1f16e5ff70ea8bdb66f492c11ae62b9b49 Mon Sep 17 00:00:00 2001 From: Inphi Date: Tue, 29 Oct 2024 13:54:50 -0700 Subject: [PATCH 076/451] cannon: Implement MIPS64Memory.sol (#12653) * cannon: Implement MIPS64Memory.sol * add non-zero revert data; cleanup go-ffi script --- packages/contracts-bedrock/.gitignore | 1 + packages/contracts-bedrock/justfile | 8 +- .../scripts/go-ffi/differential-testing.go | 87 ++++---- .../src/cannon/libraries/MIPS64Memory.sol | 176 +++++++++++++++ .../test/cannon/MIPS64Memory.t.sol | 211 ++++++++++++++++++ .../test/setup/FFIInterface.sol | 84 +++++++ 6 files changed, 524 insertions(+), 43 deletions(-) create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol create mode 100644 packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 6ca35454b291..ae6569a94ebe 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -16,6 +16,7 @@ kontrol_prove_report.xml # Scripts scripts/go-ffi/go-ffi +scripts/go-ffi/go-ffi-cannon64 # Environment Variables .envrc diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 23f91c5779c3..d77e93822462 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -27,9 +27,15 @@ forge-build: build: prebuild lint-fix-no-fail forge-build interfaces-check-no-build # Builds the go-ffi tool for contract tests. -build-go-ffi: +build-go-ffi-default: cd ./scripts/go-ffi && go build +# Builds the go-ffi tool for MIPS64 contract tests. +build-go-ffi-cannon64: + cd ./scripts/go-ffi && go build -tags=cannon64 -o ./go-ffi-cannon64 + +build-go-ffi: build-go-ffi-default build-go-ffi-cannon64 + # Cleans build artifacts and deployments. clean: rm -rf ./artifacts ./forge-artifacts ./cache ./scripts/go-ffi/go-ffi ./deployments/hardhat/* diff --git a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go index b81bbeb2b153..e35d2a82c303 100644 --- a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go +++ b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go @@ -360,68 +360,71 @@ func DiffTestUtils() { // Print the output fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProof": - // - // Generates a memory proof of `memAddr` for a trie containing memValue and memValue2 + // + // Generates memory proofs of `memAddr0` for a trie containing memValue0 and `memAddr1` for a trie containing memValue1 and memValue2 + // For the cannon stf, this is equivalent to the prestate proofs of the program counter and memory access for instruction execution mem := memory.NewMemory() if len(args) != 3 && len(args) != 5 && len(args) != 7 { panic("Error: cannonMemoryProofWithProof requires 2, 4, or 6 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) - var insnProof, memProof [896]byte + var proof1 []byte if len(args) >= 5 { - memAddr, err := strconv.ParseUint(args[3], 10, 32) + memAddr, err := strconv.ParseUint(args[3], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) + memValue, err := strconv.ParseUint(args[4], 10, arch.WordSize) checkErr(err, "Error decoding memValue") mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) - memProof = mem.MerkleProof(arch.Word(memAddr)) + proof := mem.MerkleProof(arch.Word(memAddr)) + proof1 = proof[:] } if len(args) == 7 { - memAddr, err := strconv.ParseUint(args[5], 10, 32) + memAddr, err := strconv.ParseUint(args[5], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[6], 10, 32) + memValue, err := strconv.ParseUint(args[6], 10, arch.WordSize) checkErr(err, "Error decoding memValue") mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) - memProof = mem.MerkleProof(arch.Word(memAddr)) + proof := mem.MerkleProof(arch.Word(memAddr)) + proof1 = proof[:] } - insnProof = mem.MerkleProof(arch.Word(pc)) + proof0 := mem.MerkleProof(arch.Word(memAddr0)) output := struct { MemRoot common.Hash Proof []byte }{ MemRoot: mem.MerkleRoot(), - Proof: append(insnProof[:], memProof[:]...), + Proof: append(proof0[:], proof1...), } packed, err := cannonMemoryProofArgs.Pack(&output) checkErr(err, "Error encoding output") fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProof2": - // - // Generates a memory proof of memAddr2 for a trie containing memValue + // + // Generates memory proof of `memAddr2` for a trie containing `memValue0` and `memValue1` mem := memory.NewMemory() if len(args) != 6 { panic("Error: cannonMemoryProofWithProof2 requires 5 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) - var memProof [896]byte - memAddr, err := strconv.ParseUint(args[3], 10, 32) + var memProof [memory.MemProofSize]byte + memAddr, err := strconv.ParseUint(args[3], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) - checkErr(err, "Error decoding memValue") - mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memValue1, err := strconv.ParseUint(args[4], 10, arch.WordSize) + checkErr(err, "Error decoding memValue1") + mem.SetWord(arch.Word(memAddr), arch.Word(memValue1)) - memAddr2, err := strconv.ParseUint(args[5], 10, 32) + memAddr2, err := strconv.ParseUint(args[5], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") memProof = mem.MerkleProof(arch.Word(memAddr2)) @@ -436,27 +439,27 @@ func DiffTestUtils() { checkErr(err, "Error encoding output") fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProofWrongLeaf": - // + // mem := memory.NewMemory() if len(args) != 5 { panic("Error: cannonMemoryProofWrongLeaf requires 4 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) - checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) - - var insnProof, memProof [896]byte - memAddr, err := strconv.ParseUint(args[3], 10, 32) - checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) - checkErr(err, "Error decoding memValue") - mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) + checkErr(err, "Error decoding memAddr0") + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) + + var insnProof, memProof [memory.MemProofSize]byte + memAddr1, err := strconv.ParseUint(args[3], 10, arch.WordSize) + checkErr(err, "Error decoding memAddr1") + memValue1, err := strconv.ParseUint(args[4], 10, arch.WordSize) + checkErr(err, "Error decoding memValue1") + mem.SetWord(arch.Word(memAddr1), arch.Word(memValue1)) // Compute a valid proof for the root, but for the wrong leaves. - memProof = mem.MerkleProof(arch.Word(memAddr + 32)) - insnProof = mem.MerkleProof(arch.Word(pc + 32)) + memProof = mem.MerkleProof(arch.Word(memAddr1 + arch.WordSize)) + insnProof = mem.MerkleProof(arch.Word(memAddr0 + arch.WordSize)) output := struct { MemRoot common.Hash diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol new file mode 100644 index 000000000000..23a6639bc9c4 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import "src/cannon/libraries/CannonErrors.sol"; + +library MIPS64Memory { + uint64 internal constant EXT_MASK = 0x7; + uint64 internal constant MEM_PROOF_LEAF_COUNT = 60; + uint256 internal constant U64_MASK = 0xFFFFFFFFFFFFFFFF; + + /// @notice Reads a 64-bit word from memory. + /// @param _memRoot The current memory root + /// @param _addr The address to read from. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return out_ The hashed MIPS state. + function readMem(bytes32 _memRoot, uint64 _addr, uint256 _proofOffset) internal pure returns (uint64 out_) { + bool valid; + (out_, valid) = readMemUnchecked(_memRoot, _addr, _proofOffset); + if (!valid) { + revert InvalidMemoryProof(); + } + } + + /// @notice Reads a 64-bit word from memory. + /// @param _memRoot The current memory root + /// @param _addr The address to read from. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return out_ The hashed MIPS state. + /// valid_ Whether the proof is valid. + function readMemUnchecked( + bytes32 _memRoot, + uint64 _addr, + uint256 _proofOffset + ) + internal + pure + returns (uint64 out_, bool valid_) + { + unchecked { + validateMemoryProofAvailability(_proofOffset); + assembly { + // Validate the address alignment. + if and(_addr, EXT_MASK) { + // revert InvalidAddress(); + let ptr := mload(0x40) + mstore(ptr, shl(224, 0xe6c4247b)) + revert(ptr, 0x4) + } + + // Load the leaf value. + let leaf := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + + // Convenience function to hash two nodes together in scratch space. + function hashPair(a, b) -> h { + mstore(0, a) + mstore(32, b) + h := keccak256(0, 64) + } + + // Start with the leaf node. + // Work back up by combining with siblings, to reconstruct the root. + let path := shr(5, _addr) + let node := leaf + let end := sub(MEM_PROOF_LEAF_COUNT, 1) + for { let i := 0 } lt(i, end) { i := add(i, 1) } { + let sibling := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + switch and(shr(i, path), 1) + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } + } + + // Verify the root matches. + valid_ := eq(node, _memRoot) + if valid_ { + // Bits to shift = (32 - 8 - (addr % 32)) * 8 + let shamt := shl(3, sub(sub(32, 8), and(_addr, 31))) + out_ := and(shr(shamt, leaf), U64_MASK) + } + } + } + } + + /// @notice Writes a 64-bit word to memory. + /// This function first overwrites the part of the leaf. + /// Then it recomputes the memory merkle root. + /// @param _addr The address to write to. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @param _val The value to write. + /// @return newMemRoot_ The new memory root after modification + function writeMem(uint64 _addr, uint256 _proofOffset, uint64 _val) internal pure returns (bytes32 newMemRoot_) { + unchecked { + validateMemoryProofAvailability(_proofOffset); + assembly { + // Validate the address alignment. + if and(_addr, EXT_MASK) { + // revert InvalidAddress(); + let ptr := mload(0x40) + mstore(ptr, shl(224, 0xe6c4247b)) + revert(ptr, 0x4) + } + + // Load the leaf value. + let leaf := calldataload(_proofOffset) + let shamt := shl(3, sub(sub(32, 8), and(_addr, 31))) + + // Mask out 8 bytes, and OR in the value + leaf := or(and(leaf, not(shl(shamt, U64_MASK))), shl(shamt, _val)) + _proofOffset := add(_proofOffset, 32) + + // Convenience function to hash two nodes together in scratch space. + function hashPair(a, b) -> h { + mstore(0, a) + mstore(32, b) + h := keccak256(0, 64) + } + + // Start with the leaf node. + // Work back up by combining with siblings, to reconstruct the root. + let path := shr(5, _addr) + let node := leaf + let end := sub(MEM_PROOF_LEAF_COUNT, 1) + for { let i := 0 } lt(i, end) { i := add(i, 1) } { + let sibling := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + switch and(shr(i, path), 1) + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } + } + + newMemRoot_ := node + } + return newMemRoot_; + } + } + + /// @notice Verifies a memory proof. + /// @param _memRoot The expected memory root + /// @param _addr The _addr proven. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return valid_ True iff it is a valid proof. + function isValidProof(bytes32 _memRoot, uint64 _addr, uint256 _proofOffset) internal pure returns (bool valid_) { + (, valid_) = readMemUnchecked(_memRoot, _addr, _proofOffset); + } + + /// @notice Computes the offset of a memory proof in the calldata. + /// @param _proofDataOffset The offset of the set of all memory proof data within calldata (proof.offset) + /// Equal to the offset of the first memory proof (at _proofIndex 0). + /// @param _proofIndex The index of the proof in the calldata. + /// @return offset_ The offset of the memory proof at the given _proofIndex in the calldata. + function memoryProofOffset(uint256 _proofDataOffset, uint8 _proofIndex) internal pure returns (uint256 offset_) { + unchecked { + // A proof of 64-bit memory, with 32-byte leaf values, is (64-5)=59 bytes32 entries. + // And the leaf value itself needs to be encoded as well: (59 + 1) = 60 bytes32 entries. + offset_ = _proofDataOffset + (uint256(_proofIndex) * (MEM_PROOF_LEAF_COUNT * 32)); + return offset_; + } + } + + /// @notice Validates that enough calldata is available to hold a full memory proof at the given offset + /// @param _proofStartOffset The index of the first byte of the target memory proof in calldata + function validateMemoryProofAvailability(uint256 _proofStartOffset) internal pure { + unchecked { + uint256 s = 0; + assembly { + s := calldatasize() + } + // A memory proof consists of MEM_PROOF_LEAF_COUNT bytes32 values - verify we have enough calldata + require( + s >= (_proofStartOffset + MEM_PROOF_LEAF_COUNT * 32), + "MIPS64Memory: check that there is enough calldata" + ); + } + } +} diff --git a/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol new file mode 100644 index 000000000000..a3a1160c7d48 --- /dev/null +++ b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { CommonTest } from "test/setup/CommonTest.sol"; +import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; +import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; + +contract MIPS64Memory_Test is CommonTest { + MIPS64MemoryWithCalldata mem; + + error InvalidAddress(); + + function setUp() public virtual override { + super.setUp(); + mem = new MIPS64MemoryWithCalldata(); + } + + /// @dev Static unit test for basic memory access + function test_readMem_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reading from the zero address succeeds + function test_readMemAtZero_succeeds() external { + uint64 addr = 0x0; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reading from high memory area succeeds + function test_readMemHighMem_succeeds() external { + uint64 addr = 0xFF_FF_FF_FF_00_00_00_88; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reads revert when a misaligned memory address is provided + function test_readInvalidAddress_reverts() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + vm.expectRevert(InvalidAddress.selector); + mem.readMem(root, addr + 4, 0, proof); + } + + /// @dev Static unit test asserting that reads revert when an invalid proof is provided + function test_readInvalidProof_reverts() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + vm.assertTrue(proof[64] != 0x0); // make sure the proof is tampered + proof[64] = 0x00; + vm.expectRevert(InvalidMemoryProof.selector); + mem.readMem(root, addr, 0, proof); + } + + /// @dev Static unit test asserting that reads from a non-zero proof index succeeds + function test_readMemNonZeroProofIndex_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0xFF_FF_FF_FF_00_00_00_88; + uint64 word2 = 0xF1_F2_F3_F4_F5_F6_F7_F8; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + + readWord = mem.readMem(root, addr2, 1, proof); + assertEq(readWord, word2); + } + + /// @dev Static unit test asserting basic memory write functionality + function test_writeMem_succeeds() external { + uint64 addr = 0x100; + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(addr, 0); + + uint64 word = 0x11_22_33_44_55_66_77_88; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word); + + bytes32 newRoot = mem.writeMem(addr, word, 0, zeroProof); + assertEq(newRoot, expectedRoot); + } + + // @dev Static unit test asserting that writes to high memory succeeds + function test_writeMemHighMem_succeeds() external { + uint64 addr = 0xFF_FF_FF_FF_00_00_00_88; + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(addr, 0); + + uint64 word = 0x11_22_33_44_55_66_77_88; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word); + + bytes32 newRoot = mem.writeMem(addr, word, 0, zeroProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that non-zero memory word is overwritten + function test_writeMemNonZeroProofOffset_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0x108; + uint64 word2 = 0x55_55_55_55_77_77_77_77; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 word3 = 0x44_44_44_44_44_44_44_44; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr2, word2, addr2, word3); + + bytes32 newRoot = mem.writeMem(addr2, word3, 1, initProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that a zerod memory word is set for a non-zero memory proof + function test_writeMemUniqueAccess_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0x108; + uint64 word2 = 0x55_55_55_55_77_77_77_77; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 addr3 = 0xAA_AA_AA_AA_00; + uint64 word3 = 0x44_44_44_44_44_44_44_44; + (, bytes memory addr3Proof) = ffi.getCannonMemory64Proof2(addr, word, addr2, word2, addr3); + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr2, word2, addr3, word3); + + bytes32 newRoot = mem.writeMem(addr3, word3, 0, addr3Proof); + assertEq(newRoot, expectedRoot); + + newRoot = mem.writeMem(addr3 + 8, word3, 0, addr3Proof); + assertNotEq(newRoot, expectedRoot); + + newRoot = mem.writeMem(addr3, word3 + 1, 0, addr3Proof); + assertNotEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that writes succeeds in overwriting a non-zero memory word + function test_writeMemNonZeroMem_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word); + + uint64 word2 = 0x55_55_55_55_77_77_77_77; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr + 8, word2); + + bytes32 newRoot = mem.writeMem(addr + 8, word2, 0, initProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that writes revert when a misaligned memory address is provided + function test_writeMemInvalidAddress_reverts() external { + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(0x100, 0); + vm.expectRevert(InvalidAddress.selector); + mem.writeMem(0x104, 0x0, 0, zeroProof); + } +} + +contract MIPS64MemoryWithCalldata { + function readMem( + bytes32 _root, + uint64 _addr, + uint8 _proofIndex, + bytes calldata /* _proof */ + ) + external + pure + returns (uint64 out_) + { + uint256 proofDataOffset = 4 + 32 + 32 + 32 + 32 + 32; + uint256 proofOffset = MIPS64Memory.memoryProofOffset(proofDataOffset, _proofIndex); + return MIPS64Memory.readMem(_root, _addr, proofOffset); + } + + function writeMem( + uint64 _addr, + uint64 _value, + uint8 _proofIndex, + bytes calldata /* _proof */ + ) + external + pure + returns (bytes32 root_) + { + uint256 proofDataOffset = 4 + 32 + 32 + 32 + 32 + 32; + uint256 proofOffset = MIPS64Memory.memoryProofOffset(proofDataOffset, _proofIndex); + return MIPS64Memory.writeMem(_addr, proofOffset, _value); + } +} diff --git a/packages/contracts-bedrock/test/setup/FFIInterface.sol b/packages/contracts-bedrock/test/setup/FFIInterface.sol index 727402a37c2c..c1e1612da8e6 100644 --- a/packages/contracts-bedrock/test/setup/FFIInterface.sol +++ b/packages/contracts-bedrock/test/setup/FFIInterface.sol @@ -317,6 +317,90 @@ contract FFIInterface { return (memRoot, proof); } + function getCannonMemory64Proof(uint64 addr, uint64 value) external returns (bytes32, bytes memory) { + string[] memory cmds = new string[](5); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr); + cmds[4] = vm.toString(value); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1 + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](7); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1, + uint64 memAddr2, + uint64 memVal2 + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](9); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + cmds[7] = vm.toString(memAddr2); + cmds[8] = vm.toString(memVal2); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof2( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1, + uint64 memAddrForProof + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](8); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof2"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + cmds[7] = vm.toString(memAddrForProof); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + function encodeScalarEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external returns (bytes32) { string[] memory cmds = new string[](5); cmds[0] = "scripts/go-ffi/go-ffi"; From 79ec18335edcde01653a4136515d744dc1f19f6a Mon Sep 17 00:00:00 2001 From: Inphi Date: Tue, 29 Oct 2024 14:09:29 -0700 Subject: [PATCH 077/451] cannon: Update opcode sanitizer for 64-bits (#12733) --- cannon/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cannon/Makefile b/cannon/Makefile index 7f553ad60abf..f8cb38eeda64 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -15,6 +15,9 @@ endif .DEFAULT_GOAL := cannon +# The MIPS64 r1 opcodes not supported by cannon. This list does not include coprocess-specific opcodes. +UNSUPPORTED_OPCODES := (dclo|dclz) + cannon32-impl: env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/cannon32-impl . @@ -43,7 +46,7 @@ elf: make -C ./testdata/example elf sanitize-program: - @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bltzal)'; }; then \ + @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 "$(UNSUPPORTED_OPCODES)"; }; then \ echo "guest program is sanitized for unsupported instructions"; \ else \ echo "found unsupported instructions in the guest program"; \ From d6bda0339005d98c992c749c137938d515755029 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 30 Oct 2024 07:37:51 -0700 Subject: [PATCH 078/451] cannon: Remove memory.GetUint32 (#12730) * cannon: Remove memory.GetUint32 * review comments --- cannon/cmd/run.go | 7 +++++-- cannon/mipsevm/exec/memory.go | 4 ++++ cannon/mipsevm/exec/mips_instructions.go | 6 +++++- cannon/mipsevm/memory/memory.go | 14 -------------- cannon/mipsevm/memory/memory64_test.go | 22 +--------------------- cannon/mipsevm/memory/memory_test.go | 12 ------------ cannon/mipsevm/tests/evm_common_test.go | 8 ++++---- cannon/mipsevm/testutil/memory.go | 11 +++++++---- 8 files changed, 26 insertions(+), 58 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 1a85158854b3..615e913f1e47 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + mipsexec "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" preimage "github.com/ethereum-optimism/optimism/op-preimage" @@ -410,14 +411,16 @@ func Run(ctx *cli.Context) error { if infoAt(state) { delta := time.Since(start) + pc := state.GetPC() + insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", "step", step, "pc", mipsevm.HexU32(state.GetPC()), - "insn", mipsevm.HexU32(state.GetMemory().GetUint32(state.GetPC())), + "insn", mipsevm.HexU32(insn), "ips", float64(step-startStep)/(float64(delta)/float64(time.Second)), "pages", state.GetMemory().PageCount(), "mem", state.GetMemory().Usage(), - "name", meta.LookupSymbol(state.GetPC()), + "name", meta.LookupSymbol(pc), ) } diff --git a/cannon/mipsevm/exec/memory.go b/cannon/mipsevm/exec/memory.go index 1601ad305228..8eec1ac84fa7 100644 --- a/cannon/mipsevm/exec/memory.go +++ b/cannon/mipsevm/exec/memory.go @@ -56,3 +56,7 @@ func (m *MemoryTrackerImpl) MemProof() [memory.MemProofSize]byte { func (m *MemoryTrackerImpl) MemProof2() [memory.MemProofSize]byte { return m.memProof2 } + +type NoopMemoryTracker struct{} + +func (n *NoopMemoryTracker) TrackMemAccess(Word) {} diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 39d40dd59dc0..d0d2addb3e67 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -22,7 +22,11 @@ const ( ) func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun uint32) { - insn = memory.GetUint32(pc) + if pc&0x3 != 0 { + panic(fmt.Errorf("invalid pc: %x", pc)) + } + word := memory.GetWord(pc & arch.AddressMask) + insn = uint32(SelectSubWord(pc, word, 4, false)) opcode = insn >> 26 // First 6-bits fun = insn & 0x3f // Last 6-bits diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 805267ee7fc4..e95b40ca5346 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -212,20 +212,6 @@ func (m *Memory) SetWord(addr Word, v Word) { arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v) } -// GetUint32 returns the first 32 bits located at the specified location. -func (m *Memory) GetUint32(addr Word) uint32 { - // addr must be aligned to 4 bytes - if addr&3 != 0 { - panic(fmt.Errorf("unaligned memory access: %x", addr)) - } - p, ok := m.pageLookup(addr >> PageAddrSize) - if !ok { - return 0 - } - pageAddr := addr & PageAddrMask - return binary.BigEndian.Uint32(p.Data[pageAddr : pageAddr+4]) -} - // GetWord reads the maximum sized value, [arch.Word], located at the specified address. // Note: Also referred to by the MIPS64 specification as a "double-word" memory access. func (m *Memory) GetWord(addr Word) Word { diff --git a/cannon/mipsevm/memory/memory64_test.go b/cannon/mipsevm/memory/memory64_test.go index 3bc3c3467721..38cbfd597ffc 100644 --- a/cannon/mipsevm/memory/memory64_test.go +++ b/cannon/mipsevm/memory/memory64_test.go @@ -144,41 +144,24 @@ func TestMemory64ReadWrite(t *testing.T) { m := NewMemory() m.SetWord(16, 0xAABBCCDD_EEFF1122) require.Equal(t, Word(0xAABBCCDD_EEFF1122), m.GetWord(16)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(16)) - require.Equal(t, uint32(0xEEFF1122), m.GetUint32(20)) m.SetWord(16, 0xAABB1CDD_EEFF1122) require.Equal(t, Word(0xAABB1CDD_EEFF1122), m.GetWord(16)) - require.Equal(t, uint32(0xAABB1CDD), m.GetUint32(16)) - require.Equal(t, uint32(0xEEFF1122), m.GetUint32(20)) m.SetWord(16, 0xAABB1CDD_EEFF1123) require.Equal(t, Word(0xAABB1CDD_EEFF1123), m.GetWord(16)) - require.Equal(t, uint32(0xAABB1CDD), m.GetUint32(16)) - require.Equal(t, uint32(0xEEFF1123), m.GetUint32(20)) }) t.Run("unaligned read", func(t *testing.T) { m := NewMemory() - m.SetWord(16, 0xAABBCCDD_EEFF1122) + m.SetWord(16, Word(0xAABBCCDD_EEFF1122)) m.SetWord(24, 0x11223344_55667788) for i := Word(17); i < 24; i++ { require.Panics(t, func() { m.GetWord(i) }) - if i != 20 { - require.Panics(t, func() { - m.GetUint32(i) - }) - } } require.Equal(t, Word(0x11223344_55667788), m.GetWord(24)) - require.Equal(t, uint32(0x11223344), m.GetUint32(24)) require.Equal(t, Word(0), m.GetWord(32)) - require.Equal(t, uint32(0), m.GetUint32(32)) require.Equal(t, Word(0xAABBCCDD_EEFF1122), m.GetWord(16)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(16)) - - require.Equal(t, uint32(0xEEFF1122), m.GetUint32(20)) - require.Equal(t, uint32(0x55667788), m.GetUint32(28)) }) t.Run("unaligned write", func(t *testing.T) { @@ -206,7 +189,6 @@ func TestMemory64ReadWrite(t *testing.T) { m.SetWord(23, 0x11223344) }) require.Equal(t, Word(0xAABBCCDD_EEFF1122), m.GetWord(16)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(16)) }) } @@ -218,7 +200,6 @@ func TestMemory64JSON(t *testing.T) { var res Memory require.NoError(t, json.Unmarshal(dat, &res)) require.Equal(t, Word(0xAABBCCDD_EEFF1122), res.GetWord(8)) - require.Equal(t, uint32(0xAABBCCDD), res.GetUint32(8)) } func TestMemory64Copy(t *testing.T) { @@ -226,6 +207,5 @@ func TestMemory64Copy(t *testing.T) { m.SetWord(0xAABBCCDD_8000, 0x000000_AABB) mcpy := m.Copy() require.Equal(t, Word(0xAABB), mcpy.GetWord(0xAABBCCDD_8000)) - require.Equal(t, uint32(0), mcpy.GetUint32(0xAABBCCDD_8000)) require.Equal(t, m.MerkleRoot(), mcpy.MerkleRoot()) } diff --git a/cannon/mipsevm/memory/memory_test.go b/cannon/mipsevm/memory/memory_test.go index 48e6b1d01104..3a8bdd364d80 100644 --- a/cannon/mipsevm/memory/memory_test.go +++ b/cannon/mipsevm/memory/memory_test.go @@ -125,7 +125,6 @@ func TestMemoryReadWrite(t *testing.T) { v := m.GetWord(i) expected := binary.BigEndian.Uint32(data[i : i+4]) require.Equalf(t, expected, v, "read at %d", i) - require.Equalf(t, expected, m.GetUint32(i), "read at %d", i) } }) @@ -144,10 +143,8 @@ func TestMemoryReadWrite(t *testing.T) { m := NewMemory() m.SetWord(12, 0xAABBCCDD) require.Equal(t, uint32(0xAABBCCDD), m.GetWord(12)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(12)) m.SetWord(12, 0xAABB1CDD) require.Equal(t, uint32(0xAABB1CDD), m.GetWord(12)) - require.Equal(t, uint32(0xAABB1CDD), m.GetUint32(12)) }) t.Run("unaligned read", func(t *testing.T) { @@ -156,22 +153,16 @@ func TestMemoryReadWrite(t *testing.T) { m.SetWord(16, 0x11223344) require.Panics(t, func() { m.GetWord(13) - m.GetUint32(13) }) require.Panics(t, func() { m.GetWord(14) - m.GetUint32(14) }) require.Panics(t, func() { m.GetWord(15) - m.GetUint32(15) }) require.Equal(t, uint32(0x11223344), m.GetWord(16)) - require.Equal(t, uint32(0x11223344), m.GetUint32(16)) require.Equal(t, uint32(0), m.GetWord(20)) - require.Equal(t, uint32(0), m.GetUint32(20)) require.Equal(t, uint32(0xAABBCCDD), m.GetWord(12)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(12)) }) t.Run("unaligned write", func(t *testing.T) { @@ -187,7 +178,6 @@ func TestMemoryReadWrite(t *testing.T) { m.SetWord(15, 0x11223344) }) require.Equal(t, uint32(0xAABBCCDD), m.GetWord(12)) - require.Equal(t, uint32(0xAABBCCDD), m.GetUint32(12)) }) } @@ -199,7 +189,6 @@ func TestMemoryJSON(t *testing.T) { var res Memory require.NoError(t, json.Unmarshal(dat, &res)) require.Equal(t, uint32(0xAABBCCDD), res.GetWord(8)) - require.Equal(t, uint32(0xAABBCCDD), res.GetUint32(8)) } func TestMemoryCopy(t *testing.T) { @@ -207,6 +196,5 @@ func TestMemoryCopy(t *testing.T) { m.SetWord(0x8000, 123) mcpy := m.Copy() require.Equal(t, Word(123), mcpy.GetWord(0x8000)) - require.Equal(t, Word(123), mcpy.GetUint32(0x8000)) require.Equal(t, m.MerkleRoot(), mcpy.MerkleRoot()) } diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index 932662fd6979..97cab6544ff6 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -86,7 +86,7 @@ func TestEVM(t *testing.T) { if exitGroup && goVm.GetState().GetExited() { break } - insn := state.GetMemory().GetUint32(state.GetPC()) + insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) stepWitness, err := goVm.Step(true) @@ -868,7 +868,7 @@ func TestHelloEVM(t *testing.T) { if goVm.GetState().GetExited() { break } - insn := state.GetMemory().GetUint32(state.GetPC()) + insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) if i%1000 == 0 { // avoid spamming test logs, we are executing many steps t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) } @@ -921,7 +921,7 @@ func TestClaimEVM(t *testing.T) { break } - insn := state.GetMemory().GetUint32(state.GetPC()) + insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) if i%1000 == 0 { // avoid spamming test logs, we are executing many steps t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) } @@ -969,7 +969,7 @@ func TestEntryEVM(t *testing.T) { if goVm.GetState().GetExited() { break } - insn := state.GetMemory().GetUint32(state.GetPC()) + insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) if i%10_000 == 0 { // avoid spamming test logs, we are executing many steps t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) } diff --git a/cannon/mipsevm/testutil/memory.go b/cannon/mipsevm/testutil/memory.go index 0c6fddf8f140..f3ee9cb60ad9 100644 --- a/cannon/mipsevm/testutil/memory.go +++ b/cannon/mipsevm/testutil/memory.go @@ -27,9 +27,12 @@ func StoreInstruction(mem *memory.Memory, pc Word, insn uint32) { if pc&0x3 != 0 { panic(fmt.Errorf("unaligned memory access: %x", pc)) } - exec.StoreSubWord(mem, pc, 4, Word(insn), new(noopMemTracker)) + exec.StoreSubWord(mem, pc, 4, Word(insn), new(exec.NoopMemoryTracker)) } -type noopMemTracker struct{} - -func (n *noopMemTracker) TrackMemAccess(Word) {} +func GetInstruction(mem *memory.Memory, pc Word) uint32 { + if pc&0x3 != 0 { + panic(fmt.Errorf("unaligned memory access: %x", pc)) + } + return exec.LoadSubWord(mem, pc, 4, false, new(exec.NoopMemoryTracker)) +} From 96ef7bb43f304976c5974932ed2bec72b1a9a5fe Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 30 Oct 2024 10:21:23 -0700 Subject: [PATCH 079/451] cannon: Implement 64-bit Solidity VM (#12665) * cannon: Implement MIPS64Memory.sol * cannon: Implement 64-bit Solidity VM - Implements 64-bit Cannon (with multithreading) in MIPS64.sol - Re-enable differential testing for 64-bit VMs * review comments * check pc for 4-byte alignment * gofmt * update snapshot * address nits; add more add/sub/mult overflow tests * diff test misaligned instruction * fix mul[t] MIPS64.sol emulation * diff fuzz mul operations * fix addiu test case * fix GetInstruction return value type --- cannon/Makefile | 9 +- cannon/mipsevm/exec/memory.go | 3 +- cannon/mipsevm/exec/mips_instructions.go | 6 +- cannon/mipsevm/tests/evm_common64_test.go | 36 +- cannon/mipsevm/tests/evm_common_test.go | 158 +-- .../mipsevm/tests/evm_multithreaded64_test.go | 17 +- .../mipsevm/tests/evm_multithreaded_test.go | 84 +- .../mipsevm/tests/evm_singlethreaded_test.go | 15 +- .../mipsevm/tests/fuzz_evm_common64_test.go | 140 +++ cannon/mipsevm/tests/fuzz_evm_common_test.go | 16 +- .../tests/fuzz_evm_multithreaded_test.go | 2 +- cannon/mipsevm/testutil/evm.go | 13 +- cannon/mipsevm/testutil/memory.go | 2 +- cannon/mipsevm/testutil/mips.go | 77 +- op-chain-ops/srcmap/solutil.go | 14 +- packages/contracts-bedrock/semver-lock.json | 4 + .../snapshots/abi/MIPS64.json | 93 ++ .../snapshots/storageLayout/MIPS64.json | 1 + .../contracts-bedrock/src/cannon/MIPS64.sol | 951 ++++++++++++++++++ .../src/cannon/libraries/MIPS64Arch.sol | 9 + .../cannon/libraries/MIPS64Instructions.sol | 920 +++++++++++++++++ .../src/cannon/libraries/MIPS64State.sol | 19 + .../src/cannon/libraries/MIPS64Syscalls.sol | 437 ++++++++ 23 files changed, 2825 insertions(+), 201 deletions(-) create mode 100644 cannon/mipsevm/tests/fuzz_evm_common64_test.go create mode 100644 packages/contracts-bedrock/snapshots/abi/MIPS64.json create mode 100644 packages/contracts-bedrock/snapshots/storageLayout/MIPS64.json create mode 100644 packages/contracts-bedrock/src/cannon/MIPS64.sol create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64Arch.sol create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol diff --git a/cannon/Makefile b/cannon/Makefile index f8cb38eeda64..141e7d5be85b 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -56,9 +56,12 @@ sanitize-program: contract: cd ../packages/contracts-bedrock && forge build -test: elf contract +test: elf contract test64 go test -v ./... +test64: elf contract + go test -tags=cannon64 -run '(TestEVM.*64|TestHelloEVM|TestClaimEVM)' ./mipsevm/tests + diff-%-cannon: cannon elf $$OTHER_CANNON load-elf --type $* --path ./testdata/example/bin/hello.elf --out ./bin/prestate-other.bin.gz --meta "" ./bin/cannon load-elf --type $* --path ./testdata/example/bin/hello.elf --out ./bin/prestate.bin.gz --meta "" @@ -96,6 +99,10 @@ fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests # Multi-threaded tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests + # 64-bit tests - increased fuzztime for a larger input space + go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests + go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests + go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests .PHONY: \ cannon32-impl \ diff --git a/cannon/mipsevm/exec/memory.go b/cannon/mipsevm/exec/memory.go index 8eec1ac84fa7..77c373198ddf 100644 --- a/cannon/mipsevm/exec/memory.go +++ b/cannon/mipsevm/exec/memory.go @@ -3,6 +3,7 @@ package exec import ( "fmt" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" ) @@ -37,7 +38,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr Word) { // TrackMemAccess2 creates a proof for a memory access following a call to TrackMemAccess // This is used to generate proofs for contiguous memory accesses within the same step func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr Word) { - if m.memProofEnabled && m.lastMemAccess+4 != effAddr { + if m.memProofEnabled && m.lastMemAccess+arch.WordSizeBytes != effAddr { panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess)) } m.lastMemAccess = effAddr diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index d0d2addb3e67..b72c5113e607 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -378,7 +378,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem w := uint32(SelectSubWord(rs, mem, 4, false)) val := w >> (24 - (rs&3)*8) mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) - lwrResult := ((uint32(rt) & ^mask) | val) & 0xFFFFFFFF + lwrResult := (uint32(rt) & ^mask) | val if rs&3 == 3 { // loaded bit 31 return SignExtend(Word(lwrResult), 32) } else { @@ -539,13 +539,13 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor cpu.HI = SignExtend(Word(acc>>32), 32) cpu.LO = SignExtend(Word(uint32(acc)), 32) case 0x1a: // div - if rt == 0 { + if uint32(rt) == 0 { panic("instruction divide by zero") } cpu.HI = SignExtend(Word(int32(rs)%int32(rt)), 32) cpu.LO = SignExtend(Word(int32(rs)/int32(rt)), 32) case 0x1b: // divu - if rt == 0 { + if uint32(rt) == 0 { panic("instruction divide by zero") } cpu.HI = SignExtend(Word(uint32(rs)%uint32(rt)), 32) diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go index cf51ad09038c..d83ac4fa63d1 100644 --- a/cannon/mipsevm/tests/evm_common64_test.go +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/stretchr/testify/require" ) @@ -134,12 +133,12 @@ func TestEVMSingleStep_Operators64(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } -func TestEVMSingleStep_Shift(t *testing.T) { +func TestEVMSingleStep_Shift64(t *testing.T) { cases := []struct { name string rd Word @@ -190,7 +189,8 @@ func TestEVMSingleStep_Shift(t *testing.T) { for i, tt := range cases { testName := fmt.Sprintf("%v %v", v.Name, tt.name) t.Run(testName, func(t *testing.T) { - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + pc := Word(0x0) + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(pc)) state := goVm.GetState() var insn uint32 var rtReg uint32 @@ -200,7 +200,7 @@ func TestEVMSingleStep_Shift(t *testing.T) { insn = rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct state.GetRegistersRef()[rdReg] = tt.rd state.GetRegistersRef()[rtReg] = tt.rt - testutil.StoreInstruction(state.GetMemory(), 0, insn) + testutil.StoreInstruction(state.GetMemory(), pc, insn) step := state.GetStep() // Setup expectations @@ -213,7 +213,7 @@ func TestEVMSingleStep_Shift(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } @@ -455,12 +455,12 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } -func TestEVMSingleStep_DivMult(t *testing.T) { +func TestEVMSingleStep_DivMult64(t *testing.T) { cases := []struct { name string rs Word @@ -470,6 +470,14 @@ func TestEVMSingleStep_DivMult(t *testing.T) { expectHi Word expectPanic string }{ + // TODO(#12598): Fix 32-bit tests and remove these + {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, + {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0x0), expectLo: Word(0x1)}, + {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), expectHi: Word(0xE), expectLo: Word(0xFF_FF_FF_FF_FC_FC_FD_27)}, + {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, + {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0xFF_FF_FF_FF_FF_FF_FF_FE), expectLo: Word(0x1)}, + {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_BE), expectHi: Word(0xFF_FF_FF_FF_AA_BB_CC_9F), expectLo: Word(0xFF_FF_FF_FF_FC_FD_02_9A)}, + // dmult s1, s2 // expected hi,lo were verified using qemu-mips {name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, @@ -530,6 +538,10 @@ func TestEVMSingleStep_DivMult(t *testing.T) { {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0}, {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: 2, expectLo: 0x7F_FF_FF_FF_FF_FF_FF_FF, expectHi: 1}, {name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0, expectHi: 0x7F_FF_FF_FF_00_00_00_00}, + + // a couple div/divu 64-bit edge cases + {name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"}, + {name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"}, } v := GetMultiThreadedTestCase(t) @@ -560,15 +572,13 @@ func TestEVMSingleStep_DivMult(t *testing.T) { stepWitness, err := goVm.Step(true) require.NoError(t, err) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) } }) } } -func TestEVMSingleStepBranch64(t *testing.T) { - var tracer *tracing.Hooks - +func TestEVMSingleStep_Branch64(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -651,7 +661,7 @@ func TestEVMSingleStepBranch64(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index 97cab6544ff6..be316749cd61 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -25,8 +25,6 @@ func TestEVM(t *testing.T) { testFiles, err := os.ReadDir("open_mips_tests/test/bin") require.NoError(t, err) - var tracer *tracing.Hooks // no-tracer by default, but test_util.MarkdownTracer - cases := GetMipsVersionTestCases(t) skippedTests := map[string][]string{ "multi-threaded": {"clone.bin"}, @@ -51,7 +49,6 @@ func TestEVM(t *testing.T) { expectPanic := strings.HasSuffix(f.Name(), "panic.bin") evm := testutil.NewMIPSEVM(c.Contracts) - evm.SetTracer(tracer) evm.SetLocalOracle(oracle) testutil.LogStepFailureAtCleanup(t, evm) @@ -117,8 +114,6 @@ func TestEVM(t *testing.T) { } func TestEVMSingleStep_Jump(t *testing.T) { - var tracer *tracing.Hooks - versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -157,15 +152,13 @@ func TestEVMSingleStep_Jump(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVMSingleStep_Operators(t *testing.T) { - var tracer *tracing.Hooks - versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -177,22 +170,45 @@ func TestEVMSingleStep_Operators(t *testing.T) { opcode uint32 expectRes Word }{ - {name: "add", funct: 0x20, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // add t0, s1, s2 - {name: "addu", funct: 0x21, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // addu t0, s1, s2 - {name: "addi", opcode: 0x8, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addi t0, s1, 40 - {name: "addi sign", opcode: 0x8, isImm: true, rs: Word(2), rt: Word(1), imm: uint16(0xfffe), expectRes: Word(0)}, // addi t0, s1, -2 - {name: "addiu", opcode: 0x9, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addiu t0, s1, 40 - {name: "sub", funct: 0x22, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // sub t0, s1, s2 - {name: "subu", funct: 0x23, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // subu t0, s1, s2 - {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2 - {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40 - {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2 - {name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40 - {name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2 - {name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40 - {name: "nor", funct: 0x27, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(4294965765)}, // nor t0, s1, s2 - {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2 - {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // add t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: ^Word(0), rt: ^Word(0), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(0x7F_FF_FF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: ^Word(0), rt: Word(2), expectRes: Word(1)}, // add t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: Word(2), rt: ^Word(0), expectRes: Word(1)}, // add t0, s1, s2 + {name: "add", funct: 0x20, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(1), expectRes: Word(0x80_00_00_00)}, // add t0, s1, s2 + + {name: "addu", funct: 0x21, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // addu t0, s1, s2 + {name: "addu", funct: 0x21, isImm: false, rs: ^Word(0), rt: ^Word(0), expectRes: Word(0xFF_FF_FF_FE)}, // addu t0, s1, s2 + {name: "addu", funct: 0x21, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(0x7F_FF_FF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // addu t0, s1, s2 + {name: "addu", funct: 0x21, isImm: false, rs: ^Word(0), rt: Word(2), expectRes: Word(1)}, // addu t0, s1, s2 + {name: "addu", funct: 0x21, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(1), expectRes: Word(0x80_00_00_00)}, // addu t0, s1, s2 + + {name: "addi", opcode: 0x8, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addi t0, s1, 40 + {name: "addi", opcode: 0x8, isImm: true, rs: ^Word(0), rt: Word(0xAA_BB_CC_DD), imm: uint16(1), expectRes: Word(0)}, // addi t0, s1, 40 + {name: "addi", opcode: 0x8, isImm: true, rs: ^Word(0), rt: Word(0xAA_BB_CC_DD), imm: uint16(0xFF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // addi t0, s1, 40 + {name: "addi sign", opcode: 0x8, isImm: true, rs: Word(2), rt: Word(1), imm: uint16(0xfffe), expectRes: Word(0)}, // addi t0, s1, -2 + + {name: "addiu", opcode: 0x9, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addiu t0, s1, 40 + {name: "addiu", opcode: 0x9, isImm: true, rs: ^Word(0), rt: Word(0xAA_BB_CC_DD), imm: uint16(1), expectRes: Word(0)}, // addiu t0, s1, 40 + {name: "addiu", opcode: 0x9, isImm: true, rs: ^Word(0), rt: Word(0xAA_BB_CC_DD), imm: uint16(0xFF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // addiu t0, s1, 40 + + {name: "sub", funct: 0x22, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // sub t0, s1, s2 + {name: "sub", funct: 0x22, isImm: false, rs: ^Word(0), rt: Word(1), expectRes: Word(0xFF_FF_FF_FE)}, // sub t0, s1, s2 + {name: "sub", funct: 0x22, isImm: false, rs: Word(1), rt: ^Word(0), expectRes: Word(0x2)}, // sub t0, s1, s2 + + {name: "subu", funct: 0x23, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // subu t0, s1, s2 + {name: "subu", funct: 0x23, isImm: false, rs: ^Word(0), rt: Word(1), expectRes: Word(0xFF_FF_FF_FE)}, // subu t0, s1, s2 + {name: "subu", funct: 0x23, isImm: false, rs: Word(1), rt: ^Word(0), expectRes: Word(0x2)}, // subu t0, s1, s2 + + {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2 + {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40 + {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2 + {name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40 + {name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2 + {name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40 + {name: "nor", funct: 0x27, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(4294965765)}, // nor t0, s1, s2 + {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2 + {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2 } for _, v := range versions { @@ -236,15 +252,13 @@ func TestEVMSingleStep_Operators(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVMSingleStep_LoadStore(t *testing.T) { - var tracer *tracing.Hooks - loadMemVal := Word(0x11_22_33_44) loadMemValNeg := Word(0xF1_F2_F3_F4) rtVal := Word(0xaa_bb_cc_dd) @@ -330,14 +344,13 @@ func TestEVMSingleStep_LoadStore(t *testing.T) { // Validate expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVMSingleStep_MovzMovn(t *testing.T) { - var tracer *tracing.Hooks versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -376,7 +389,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) if tt.funct == 0xa { t2 = 0x1 @@ -392,7 +405,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } @@ -400,7 +413,6 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { } func TestEVMSingleStep_MfhiMflo(t *testing.T) { - var tracer *tracing.Hooks versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -430,7 +442,7 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) { require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } @@ -453,9 +465,21 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { expectRevert string errMsg string }{ - {name: "mul", funct: uint32(0x2), rs: Word(5), rt: Word(2), opcode: uint32(28), rdReg: uint32(0x8), expectRes: Word(10)}, // mul t0, t1, t2 - {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // mult t1, t2 - {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // multu t1, t2 + {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(5), rt: Word(2), rdReg: uint32(0x8), expectRes: Word(10)}, // mul t0, t1, t2 + {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0x1), rt: ^Word(0), rdReg: uint32(0x8), expectRes: ^Word(0)}, // mul t1, t2 + {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x8), expectRes: Word(0x1)}, // mul t1, t2 + {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), rdReg: uint32(0x8), expectRes: Word(0xFC_FC_FD_27)}, // mul t1, t2 + + {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // mult t1, t2 + {name: "mult", funct: uint32(0x18), rs: Word(0x1), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xFF_FF_FF_FF), expectLo: Word(0xFF_FF_FF_FF)}, // mult t1, t2 + {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0), expectLo: Word(0x1)}, // mult t1, t2 + {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xE), expectLo: Word(0xFC_FC_FD_27)}, // mult t1, t2 + + {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, // multu t1, t2 + {name: "multu", funct: uint32(0x19), rs: Word(0x1), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x0), expectLo: Word(0xFF_FF_FF_FF)}, // multu t1, t2 + {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xFF_FF_FF_FE), expectLo: Word(0x1)}, // multu t1, t2 + {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xAA_BB_CC_BE), expectLo: Word(0xFC_FC_FD_27)}, // multu t1, t2 + {name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2 {name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // div t1, t2 {name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2 @@ -483,7 +507,7 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { _, _ = goVm.Step( false) }) - testutil.AssertEVMReverts(t, state, v.Contracts, tracer, proofData, tt.errMsg) + testutil.AssertEVMReverts(t, state, v.Contracts, tracer, proofData, testutil.CreateErrorStringMatcher(tt.errMsg)) return } @@ -503,14 +527,13 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVMSingleStep_MthiMtlo(t *testing.T) { - var tracer *tracing.Hooks versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -544,15 +567,13 @@ func TestEVMSingleStep_MthiMtlo(t *testing.T) { require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVM_MMap(t *testing.T) { - var tracer *tracing.Hooks - versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -609,15 +630,13 @@ func TestEVM_MMap(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } } func TestEVMSysWriteHint(t *testing.T) { - var tracer *tracing.Hooks - versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -804,7 +823,7 @@ func TestEVMSysWriteHint(t *testing.T) { expected.Validate(t, state) require.Equal(t, tt.expectedHints, oracle.Hints()) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } @@ -814,23 +833,38 @@ func TestEVMFault(t *testing.T) { var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer versions := GetMipsVersionTestCases(t) + + misAlignedInstructionErr := func() testutil.ErrMatcher { + if arch.IsMips32 { + // matches revert(0,0) + return testutil.CreateNoopErrorMatcher() + } else { + return testutil.CreateCustomErrorMatcher("InvalidPC()") + } + } + cases := []struct { name string + pc arch.Word nextPC arch.Word insn uint32 - errMsg string + errMsg testutil.ErrMatcher memoryProofAddresses []Word }{ - {"illegal instruction", 0, 0xFF_FF_FF_FF, "invalid instruction", []Word{0xa7ef00cc}}, - {"branch in delay-slot", 8, 0x11_02_00_03, "branch in delay slot", []Word{}}, - {"jump in delay-slot", 8, 0x0c_00_00_0c, "jump in delay slot", []Word{}}, + {name: "illegal instruction", nextPC: 0, insn: 0xFF_FF_FF_FF, errMsg: testutil.CreateErrorStringMatcher("invalid instruction"), memoryProofAddresses: []Word{0xa7ef00cc}}, + {name: "branch in delay-slot", nextPC: 8, insn: 0x11_02_00_03, errMsg: testutil.CreateErrorStringMatcher("branch in delay slot")}, + {name: "jump in delay-slot", nextPC: 8, insn: 0x0c_00_00_0c, errMsg: testutil.CreateErrorStringMatcher("jump in delay slot")}, + {name: "misaligned instruction", pc: 1, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()}, + {name: "misaligned instruction", pc: 2, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()}, + {name: "misaligned instruction", pc: 3, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()}, + {name: "misaligned instruction", pc: 5, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()}, } for _, v := range versions { for _, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithNextPC(tt.nextPC)) + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC)) state := goVm.GetState() testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) // set the return address ($ra) to jump into when test completes @@ -846,7 +880,6 @@ func TestEVMFault(t *testing.T) { func TestHelloEVM(t *testing.T) { t.Parallel() - var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer versions := GetMipsVersionTestCases(t) for _, v := range versions { @@ -854,22 +887,21 @@ func TestHelloEVM(t *testing.T) { t.Run(v.Name, func(t *testing.T) { t.Parallel() evm := testutil.NewMIPSEVM(v.Contracts) - evm.SetTracer(tracer) testutil.LogStepFailureAtCleanup(t, evm) var stdOutBuf, stdErrBuf bytes.Buffer - elfFile := "../../testdata/example/bin/hello.elf" + elfFile := testutil.ProgramPath("hello") goVm := v.ElfVMFactory(t, elfFile, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger()) state := goVm.GetState() start := time.Now() - for i := 0; i < 400_000; i++ { + for i := 0; i < 430_000; i++ { step := goVm.GetState().GetStep() if goVm.GetState().GetExited() { break } insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) - if i%1000 == 0 { // avoid spamming test logs, we are executing many steps + if i%10_000 == 0 { // avoid spamming test logs, we are executing many steps t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) } @@ -897,7 +929,6 @@ func TestHelloEVM(t *testing.T) { func TestClaimEVM(t *testing.T) { t.Parallel() - var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer versions := GetMipsVersionTestCases(t) for _, v := range versions { @@ -905,13 +936,12 @@ func TestClaimEVM(t *testing.T) { t.Run(v.Name, func(t *testing.T) { t.Parallel() evm := testutil.NewMIPSEVM(v.Contracts) - evm.SetTracer(tracer) testutil.LogStepFailureAtCleanup(t, evm) oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t) var stdOutBuf, stdErrBuf bytes.Buffer - elfFile := "../../testdata/example/bin/claim.elf" + elfFile := testutil.ProgramPath("claim") goVm := v.ElfVMFactory(t, elfFile, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger()) state := goVm.GetState() @@ -922,7 +952,7 @@ func TestClaimEVM(t *testing.T) { } insn := testutil.GetInstruction(state.GetMemory(), state.GetPC()) - if i%1000 == 0 { // avoid spamming test logs, we are executing many steps + if i%10_000 == 0 { // avoid spamming test logs, we are executing many steps t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn) } @@ -947,7 +977,6 @@ func TestClaimEVM(t *testing.T) { func TestEntryEVM(t *testing.T) { t.Parallel() - var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer versions := GetMipsVersionTestCases(t) for _, v := range versions { @@ -955,11 +984,10 @@ func TestEntryEVM(t *testing.T) { t.Run(v.Name, func(t *testing.T) { t.Parallel() evm := testutil.NewMIPSEVM(v.Contracts) - evm.SetTracer(tracer) testutil.LogStepFailureAtCleanup(t, evm) var stdOutBuf, stdErrBuf bytes.Buffer - elfFile := "../../testdata/example/bin/entry.elf" + elfFile := testutil.ProgramPath("entry") goVm := v.ElfVMFactory(t, elfFile, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger()) state := goVm.GetState() @@ -993,8 +1021,6 @@ func TestEntryEVM(t *testing.T) { } func TestEVMSingleStepBranch(t *testing.T) { - var tracer *tracing.Hooks - versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -1086,7 +1112,7 @@ func TestEVMSingleStepBranch(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } diff --git a/cannon/mipsevm/tests/evm_multithreaded64_test.go b/cannon/mipsevm/tests/evm_multithreaded64_test.go index 9af5f212a221..5c5963a0e027 100644 --- a/cannon/mipsevm/tests/evm_multithreaded64_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded64_test.go @@ -8,7 +8,6 @@ import ( "fmt" "testing" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" @@ -18,8 +17,6 @@ import ( ) func TestEVM_MT64_LL(t *testing.T) { - var tracer *tracing.Hooks - memVal := Word(0x11223344_55667788) memValNeg := Word(0xF1223344_F5667788) cases := []struct { @@ -84,15 +81,13 @@ func TestEVM_MT64_LL(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_MT64_SC(t *testing.T) { - var tracer *tracing.Hooks - llVariations := []struct { name string llReservationStatus multithreaded.LLReservationStatus @@ -187,15 +182,13 @@ func TestEVM_MT64_SC(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_MT64_LLD(t *testing.T) { - var tracer *tracing.Hooks - memVal := Word(0x11223344_55667788) memValNeg := Word(0xF1223344_F5667788) cases := []struct { @@ -260,15 +253,13 @@ func TestEVM_MT64_LLD(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_MT64_SCD(t *testing.T) { - var tracer *tracing.Hooks - value := Word(0x11223344_55667788) llVariations := []struct { name string @@ -364,7 +355,7 @@ func TestEVM_MT64_SCD(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 4b663f3b7f11..1e1704558abd 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -26,8 +26,6 @@ import ( type Word = arch.Word func TestEVM_MT_LL(t *testing.T) { - var tracer *tracing.Hooks - // Set up some test values that will be reused posValue := uint64(0xAAAA_BBBB_1122_3344) posValueRet := uint64(0x1122_3344) @@ -90,15 +88,13 @@ func TestEVM_MT_LL(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_MT_SC(t *testing.T) { - var tracer *tracing.Hooks - // Set up some test values that will be reused memValue := uint64(0x1122_3344_5566_7788) @@ -189,15 +185,13 @@ func TestEVM_MT_SC(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_MT_SysRead_Preimage(t *testing.T) { - var tracer *tracing.Hooks - preimageValue := make([]byte, 0, 8) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32) @@ -290,14 +284,14 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { if c.shouldPanic { require.Panics(t, func() { _, _ = goVm.Step(true) }) - testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts, tracer) + testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts) } else { stepWitness, err := goVm.Step(true) require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } }) } @@ -305,8 +299,6 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { } func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { - var tracer *tracing.Hooks - llVariations := []struct { name string llReservationStatus multithreaded.LLReservationStatus @@ -382,7 +374,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } @@ -390,7 +382,6 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { func TestEVM_SysClone_FlagHandling(t *testing.T) { contracts := testutil.TestContractsSetup(t, testutil.MipsMultithreaded) - var tracer *tracing.Hooks cases := []struct { name string @@ -437,7 +428,6 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { } evm := testutil.NewMIPSEVM(contracts) - evm.SetTracer(tracer) testutil.LogStepFailureAtCleanup(t, evm) evmPost := evm.Step(t, stepWitness, curStep, multithreaded.GetStateHashFn()) @@ -449,7 +439,6 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { } func TestEVM_SysClone_Successful(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string traverseRight bool @@ -506,13 +495,12 @@ func TestEVM_SysClone_Successful(t *testing.T) { activeStack, inactiveStack := mttestutil.GetThreadStacks(state) require.Equal(t, 2, len(activeStack)) require.Equal(t, 0, len(inactiveStack)) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SysGetTID(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string threadId Word @@ -545,13 +533,12 @@ func TestEVM_SysGetTID(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SysExit(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string threadCount int @@ -594,13 +581,12 @@ func TestEVM_SysExit(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_PopExitedThread(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string traverseRight bool @@ -646,13 +632,12 @@ func TestEVM_PopExitedThread(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SysFutex_WaitPrivate(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string addressParam Word @@ -713,13 +698,12 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SysFutex_WakePrivate(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string addressParam Word @@ -776,14 +760,12 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { - var tracer *tracing.Hooks - // From: https://github.com/torvalds/linux/blob/5be63fc19fcaa4c236b307420483578a56986a37/include/uapi/linux/futex.h const FUTEX_PRIVATE_FLAG = 128 const FUTEX_WAIT = 0 @@ -855,7 +837,7 @@ func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } @@ -869,7 +851,6 @@ func TestEVM_SysNanosleep(t *testing.T) { } func runPreemptSyscall(t *testing.T, syscallName string, syscallNum uint32) { - var tracer *tracing.Hooks cases := []struct { name string traverseRight bool @@ -908,15 +889,13 @@ func runPreemptSyscall(t *testing.T, syscallName string, syscallNum uint32) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_SysOpen(t *testing.T) { - var tracer *tracing.Hooks - goVm, state, contracts := setup(t, 5512, nil) testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) @@ -937,11 +916,10 @@ func TestEVM_SysOpen(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } func TestEVM_SysGetPID(t *testing.T) { - var tracer *tracing.Hooks goVm, state, contracts := setup(t, 1929, nil) testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) @@ -962,7 +940,7 @@ func TestEVM_SysGetPID(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } func TestEVM_SysClockGettimeMonotonic(t *testing.T) { @@ -974,8 +952,6 @@ func TestEVM_SysClockGettimeRealtime(t *testing.T) { } func testEVM_SysClockGettime(t *testing.T, clkid Word) { - var tracer *tracing.Hooks - llVariations := []struct { name string llReservationStatus multithreaded.LLReservationStatus @@ -1062,14 +1038,13 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } } func TestEVM_SysClockGettimeNonMonotonic(t *testing.T) { - var tracer *tracing.Hooks goVm, state, contracts := setup(t, 2101, nil) timespecAddr := Word(0x1000) @@ -1091,7 +1066,7 @@ func TestEVM_SysClockGettimeNonMonotonic(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } var NoopSyscalls = map[string]uint32{ @@ -1132,7 +1107,6 @@ var NoopSyscalls = map[string]uint32{ } func TestEVM_NoopSyscall(t *testing.T) { - var tracer *tracing.Hooks for noopName, noopVal := range NoopSyscalls { t.Run(noopName, func(t *testing.T) { goVm, state, contracts := setup(t, int(noopVal), nil) @@ -1155,7 +1129,7 @@ func TestEVM_NoopSyscall(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } @@ -1191,7 +1165,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) { require.Panics(t, func() { _, _ = goVm.Step(true) }) errorMessage := "MIPS2: unimplemented syscall" - testutil.AssertEVMReverts(t, state, contracts, tracer, proofData, errorMessage) + testutil.AssertEVMReverts(t, state, contracts, tracer, proofData, testutil.CreateErrorStringMatcher(errorMessage)) }) } } @@ -1223,14 +1197,13 @@ func TestEVM_EmptyThreadStacks(t *testing.T) { require.PanicsWithValue(t, "Active thread stack is empty", func() { _, _ = goVm.Step(false) }) errorMessage := "MIPS2: active thread stack is empty" - testutil.AssertEVMReverts(t, state, contracts, tracer, proofCase.Proof, errorMessage) + testutil.AssertEVMReverts(t, state, contracts, tracer, proofCase.Proof, testutil.CreateErrorStringMatcher(errorMessage)) }) } } } func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string step uint64 @@ -1307,7 +1280,7 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, c.step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, c.step, goVm, multithreaded.GetStateHashFn(), contracts) }) } @@ -1315,7 +1288,6 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { } func TestEVM_NormalTraversal_Full(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string threadCount int @@ -1357,7 +1329,7 @@ func TestEVM_NormalTraversal_Full(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } // We should be back to the original state with only a few modifications @@ -1372,7 +1344,6 @@ func TestEVM_NormalTraversal_Full(t *testing.T) { func TestEVM_WakeupTraversalStep(t *testing.T) { addr := Word(0x1234) wakeupVal := Word(0x999) - var tracer *tracing.Hooks cases := []struct { name string wakeupAddr Word @@ -1440,13 +1411,12 @@ func TestEVM_WakeupTraversalStep(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_WakeupTraversal_Full(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string threadCount int @@ -1486,7 +1456,7 @@ func TestEVM_WakeupTraversal_Full(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) } // We should be back to the original state with only a few modifications @@ -1499,7 +1469,6 @@ func TestEVM_WakeupTraversal_Full(t *testing.T) { } func TestEVM_WakeupTraversal_WithExitedThreads(t *testing.T) { - var tracer *tracing.Hooks addr := Word(0x1234) wakeupVal := Word(0x999) cases := []struct { @@ -1567,13 +1536,12 @@ func TestEVM_WakeupTraversal_WithExitedThreads(t *testing.T) { stepWitness, err = goVm.Step(true) require.NoError(t, err) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } func TestEVM_SchedQuantumThreshold(t *testing.T) { - var tracer *tracing.Hooks cases := []struct { name string stepsSinceLastContextSwitch uint64 @@ -1613,7 +1581,7 @@ func TestEVM_SchedQuantumThreshold(t *testing.T) { // Validate post-state expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } } diff --git a/cannon/mipsevm/tests/evm_singlethreaded_test.go b/cannon/mipsevm/tests/evm_singlethreaded_test.go index 159bc2bf4dbd..f13ac5a68fa8 100644 --- a/cannon/mipsevm/tests/evm_singlethreaded_test.go +++ b/cannon/mipsevm/tests/evm_singlethreaded_test.go @@ -5,7 +5,6 @@ import ( "os" "testing" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -17,8 +16,6 @@ import ( ) func TestEVM_LL(t *testing.T) { - var tracer *tracing.Hooks - cases := []struct { name string base Word @@ -61,14 +58,12 @@ func TestEVM_LL(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } func TestEVM_SC(t *testing.T) { - var tracer *tracing.Hooks - cases := []struct { name string base Word @@ -115,14 +110,12 @@ func TestEVM_SC(t *testing.T) { // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } } func TestEVM_SysRead_Preimage(t *testing.T) { - var tracer *tracing.Hooks - preimageValue := make([]byte, 0, 8) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32) @@ -183,14 +176,14 @@ func TestEVM_SysRead_Preimage(t *testing.T) { if c.shouldPanic { require.Panics(t, func() { _, _ = goVm.Step(true) }) - testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, v.Contracts, tracer) + testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, v.Contracts) } else { stepWitness, err := goVm.Step(true) require.NoError(t, err) // Check expectations expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) } }) } diff --git a/cannon/mipsevm/tests/fuzz_evm_common64_test.go b/cannon/mipsevm/tests/fuzz_evm_common64_test.go new file mode 100644 index 000000000000..89aceb41a4e6 --- /dev/null +++ b/cannon/mipsevm/tests/fuzz_evm_common64_test.go @@ -0,0 +1,140 @@ +//go:build cannon64 +// +build cannon64 + +package tests + +import ( + "os" + "testing" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" + "github.com/stretchr/testify/require" +) + +func FuzzStateConsistencyMulOp(f *testing.F) { + f.Add(int64(0x80_00_00_00), int64(0x80_00_00_00), int64(1)) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_11_22_33_44)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_11_22_33_44)), + int64(1), + ) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_80_00_00_00)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_80_00_00_00)), + int64(1), + ) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_FF_FF_FF_FF)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_FF_FF_FF_FF)), + int64(1), + ) + + const opcode uint32 = 28 + const mulFunct uint32 = 0x2 + versions := GetMipsVersionTestCases(f) + f.Fuzz(func(t *testing.T, rs int64, rt int64, seed int64) { + for _, v := range versions { + t.Run(v.Name, func(t *testing.T) { + mulOpConsistencyCheck(t, versions, opcode, true, mulFunct, Word(rs), Word(rt), seed) + }) + } + }) +} + +func FuzzStateConsistencyMultOp(f *testing.F) { + f.Add(int64(0x80_00_00_00), int64(0x80_00_00_00), int64(1)) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_11_22_33_44)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_11_22_33_44)), + int64(1), + ) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_80_00_00_00)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_80_00_00_00)), + int64(1), + ) + f.Add( + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_FF_FF_FF_FF)), + testutil.ToSignedInteger(uint64(0xFF_FF_FF_FF_FF_FF_FF_FF)), + int64(1), + ) + + const multFunct uint32 = 0x18 + versions := GetMipsVersionTestCases(f) + f.Fuzz(func(t *testing.T, rs int64, rt int64, seed int64) { + mulOpConsistencyCheck(t, versions, 0, false, multFunct, Word(rs), Word(rt), seed) + }) +} + +func FuzzStateConsistencyMultuOp(f *testing.F) { + f.Add(uint64(0x80_00_00_00), uint64(0x80_00_00_00), int64(1)) + f.Add( + uint64(0xFF_FF_FF_FF_11_22_33_44), + uint64(0xFF_FF_FF_FF_11_22_33_44), + int64(1), + ) + f.Add( + uint64(0xFF_FF_FF_FF_80_00_00_00), + uint64(0xFF_FF_FF_FF_80_00_00_00), + int64(1), + ) + f.Add( + uint64(0xFF_FF_FF_FF_FF_FF_FF_FF), + uint64(0xFF_FF_FF_FF_FF_FF_FF_FF), + int64(1), + ) + + const multuFunct uint32 = 0x19 + versions := GetMipsVersionTestCases(f) + f.Fuzz(func(t *testing.T, rs uint64, rt uint64, seed int64) { + mulOpConsistencyCheck(t, versions, 0, false, multuFunct, rs, rt, seed) + }) +} + +type insn struct { + opcode uint32 + expectRdReg bool + funct uint32 +} + +func mulOpConsistencyCheck( + t *testing.T, versions []VersionedVMTestCase, + opcode uint32, expectRdReg bool, funct uint32, + rs Word, rt Word, seed int64) { + for _, v := range versions { + t.Run(v.Name, func(t *testing.T) { + rsReg := uint32(17) + rtReg := uint32(18) + rdReg := uint32(0) + if expectRdReg { + rdReg = 19 + } + + insn := opcode<<26 | rsReg<<21 | rtReg<<16 | rdReg<<11 | funct + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + state.GetRegistersRef()[rsReg] = rs + state.GetRegistersRef()[rtReg] = rt + testutil.StoreInstruction(state.GetMemory(), 0, insn) + step := state.GetStep() + + // mere sanity checks + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // use the post-state rdReg or LO and HI just so we can run sanity checks + if expectRdReg { + expected.Registers[rdReg] = state.GetRegistersRef()[rdReg] + } else { + expected.LO = state.GetCpu().LO + expected.HI = state.GetCpu().HI + } + expected.Validate(t, state) + + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } +} diff --git a/cannon/mipsevm/tests/fuzz_evm_common_test.go b/cannon/mipsevm/tests/fuzz_evm_common_test.go index 2c992b5b7a3f..d1d10fbeb3cb 100644 --- a/cannon/mipsevm/tests/fuzz_evm_common_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_common_test.go @@ -42,7 +42,7 @@ func FuzzStateSyscallBrk(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -97,7 +97,7 @@ func FuzzStateSyscallMmap(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -126,7 +126,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -182,7 +182,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -219,7 +219,7 @@ func FuzzStateHintRead(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -282,7 +282,7 @@ func FuzzStatePreimageRead(f *testing.F) { require.True(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -364,7 +364,7 @@ func FuzzStateHintWrite(f *testing.F) { // Validate require.Equal(t, expectedHints, oracle.Hints()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) @@ -424,7 +424,7 @@ func FuzzStatePreimageWrite(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } }) diff --git a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go index 09ad014189fe..49f45a4a5f2c 100644 --- a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go @@ -62,6 +62,6 @@ func FuzzStateSyscallCloneMT(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), v.Contracts, nil) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), v.Contracts) }) } diff --git a/cannon/mipsevm/testutil/evm.go b/cannon/mipsevm/testutil/evm.go index 6d424ae62493..6a4832c2953d 100644 --- a/cannon/mipsevm/testutil/evm.go +++ b/cannon/mipsevm/testutil/evm.go @@ -7,6 +7,7 @@ import ( "math/big" "os" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/op-chain-ops/srcmap" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -66,7 +67,11 @@ func loadArtifacts(version MipsVersion) (*Artifacts, error) { case MipsSingleThreaded: mips, err = artifactFS.ReadArtifact("MIPS.sol", "MIPS") case MipsMultithreaded: - mips, err = artifactFS.ReadArtifact("MIPS2.sol", "MIPS2") + if arch.IsMips32 { + mips, err = artifactFS.ReadArtifact("MIPS2.sol", "MIPS2") + } else { + mips, err = artifactFS.ReadArtifact("MIPS64.sol", "MIPS64") + } default: return nil, fmt.Errorf("Unknown MipsVersion supplied: %v", version) } @@ -167,7 +172,11 @@ func SourceMapTracer(t require.TestingT, version MipsVersion, mips *foundry.Arti case MipsSingleThreaded: mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS") case MipsMultithreaded: - mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS2") + if arch.IsMips32 { + mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS2") + } else { + mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS64") + } default: require.Fail(t, "invalid mips version") } diff --git a/cannon/mipsevm/testutil/memory.go b/cannon/mipsevm/testutil/memory.go index f3ee9cb60ad9..ca41453aaf62 100644 --- a/cannon/mipsevm/testutil/memory.go +++ b/cannon/mipsevm/testutil/memory.go @@ -34,5 +34,5 @@ func GetInstruction(mem *memory.Memory, pc Word) uint32 { if pc&0x3 != 0 { panic(fmt.Errorf("unaligned memory access: %x", pc)) } - return exec.LoadSubWord(mem, pc, 4, false, new(exec.NoopMemoryTracker)) + return uint32(exec.LoadSubWord(mem, pc, 4, false, new(exec.NoopMemoryTracker))) } diff --git a/cannon/mipsevm/testutil/mips.go b/cannon/mipsevm/testutil/mips.go index 3ce38809c9aa..9ba37cb30e2a 100644 --- a/cannon/mipsevm/testutil/mips.go +++ b/cannon/mipsevm/testutil/mips.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm" @@ -22,6 +23,9 @@ import ( preimage "github.com/ethereum-optimism/optimism/op-preimage" ) +// maxStepGas should be less than the L1 gas limit +const maxStepGas = 20_000_000 + type MIPSEVM struct { sender vm.AccountRef startingGas uint64 @@ -36,11 +40,29 @@ type MIPSEVM struct { lastPreimageOracleInput []byte } -func NewMIPSEVM(contracts *ContractMetadata) *MIPSEVM { +func NewMIPSEVM(contracts *ContractMetadata, opts ...evmOption) *MIPSEVM { env, evmState := NewEVMEnv(contracts) sender := vm.AccountRef{0x13, 0x37} - startingGas := uint64(30_000_000) - return &MIPSEVM{sender, startingGas, env, evmState, contracts.Addresses, nil, contracts.Artifacts, math.MaxUint64, nil, nil} + startingGas := uint64(maxStepGas) + evm := &MIPSEVM{sender, startingGas, env, evmState, contracts.Addresses, nil, contracts.Artifacts, math.MaxUint64, nil, nil} + for _, opt := range opts { + opt(evm) + } + return evm +} + +type evmOption func(c *MIPSEVM) + +func WithSourceMapTracer(t *testing.T, ver MipsVersion) evmOption { + return func(evm *MIPSEVM) { + evm.SetSourceMapTracer(t, ver) + } +} + +func WithTracingHooks(tracer *tracing.Hooks) evmOption { + return func(evm *MIPSEVM) { + evm.SetTracer(tracer) + } } func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) { @@ -171,15 +193,8 @@ func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) { } // ValidateEVM runs a single evm step and validates against an FPVM poststate -func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, tracer *tracing.Hooks) { - if !arch.IsMips32 { - // TODO(#12250) Re-enable EVM validation once 64-bit MIPS contracts are completed - t.Logf("WARNING: Skipping EVM validation for 64-bit MIPS") - return - } - - evm := NewMIPSEVM(contracts) - evm.SetTracer(tracer) +func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) { + evm := NewMIPSEVM(contracts, opts...) LogStepFailureAtCleanup(t, evm) evmPost := evm.Step(t, stepWitness, step, hashFn) @@ -188,15 +203,39 @@ func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, go "mipsevm produced different state than EVM") } +type ErrMatcher func(*testing.T, []byte) + +func CreateNoopErrorMatcher() ErrMatcher { + return func(t *testing.T, ret []byte) {} +} + +// CreateErrorStringMatcher matches an Error(string) +func CreateErrorStringMatcher(expect string) ErrMatcher { + return func(t *testing.T, ret []byte) { + require.Greaterf(t, len(ret), 4, "Return data length should be greater than 4 bytes: %x", ret) + unpacked, decodeErr := abi.UnpackRevert(ret) + require.NoError(t, decodeErr, "Failed to unpack revert reason") + require.Contains(t, unpacked, expect, "Revert reason mismatch") + } +} + +// CreateCustomErrorMatcher matches a custom error given the error signature +func CreateCustomErrorMatcher(sig string) ErrMatcher { + return func(t *testing.T, ret []byte) { + expect := crypto.Keccak256([]byte(sig))[:4] + require.EqualValuesf(t, expect, ret, "return value is %x", ret) + } +} + // AssertEVMReverts runs a single evm step from an FPVM prestate and asserts that the VM panics -func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *ContractMetadata, tracer *tracing.Hooks, ProofData []byte, expectedReason string) { +func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *ContractMetadata, tracer *tracing.Hooks, ProofData []byte, matcher ErrMatcher) { encodedWitness, _ := state.EncodeWitness() stepWitness := &mipsevm.StepWitness{ State: encodedWitness, ProofData: ProofData, } input := EncodeStepInput(t, stepWitness, mipsevm.LocalContext{}, contracts.Artifacts.MIPS) - startingGas := uint64(30_000_000) + startingGas := uint64(maxStepGas) env, evmState := NewEVMEnv(contracts) env.Config.Tracer = tracer @@ -204,18 +243,14 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract ret, _, err := env.Call(vm.AccountRef(sender), contracts.Addresses.MIPS, input, startingGas, common.U2560) require.EqualValues(t, err, vm.ErrExecutionReverted) - require.Greater(t, len(ret), 4, "Return data length should be greater than 4 bytes") - unpacked, decodeErr := abi.UnpackRevert(ret) - require.NoError(t, decodeErr, "Failed to unpack revert reason") - require.Equal(t, expectedReason, unpacked, "Revert reason mismatch") + matcher(t, ret) logs := evmState.Logs() require.Equal(t, 0, len(logs)) } -func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, tracer *tracing.Hooks) { - evm := NewMIPSEVM(contracts) - evm.SetTracer(tracer) +func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, opts ...evmOption) { + evm := NewMIPSEVM(contracts, opts...) LogStepFailureAtCleanup(t, evm) evm.assertPreimageOracleReverts(t, preimageKey, preimageValue, preimageOffset) diff --git a/op-chain-ops/srcmap/solutil.go b/op-chain-ops/srcmap/solutil.go index 6d1aa4c64a3e..929632cbfd5f 100644 --- a/op-chain-ops/srcmap/solutil.go +++ b/op-chain-ops/srcmap/solutil.go @@ -238,6 +238,16 @@ func (s *SourceMapTracer) info(codeAddr common.Address, pc uint64) string { func (s *SourceMapTracer) OnOpCode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { op := vm.OpCode(opcode) + var top []string + stk := scope.StackData() + for i := len(stk) - 1; i >= 0; i-- { + top = append(top, stk[i].Hex()) + if len(top) == 4 { + break + } + } + stkInfo := fmt.Sprintf("[%s]", strings.Join(top, ", ")) + if op.IsPush() { var val []byte sc, ok := scope.(*vm.ScopeContext) @@ -248,10 +258,10 @@ func (s *SourceMapTracer) OnOpCode(pc uint64, opcode byte, gas, cost uint64, sco } else { val = []byte("N/A") } - fmt.Fprintf(s.out, "%-40s : pc %x opcode %s (%x)\n", s.info(scope.Address(), pc), pc, op.String(), val) + fmt.Fprintf(s.out, "%-40s : pc %x opcode %s (%x) \t| stk[:%d] %s\n", s.info(scope.Address(), pc), pc, op.String(), val, len(top), stkInfo) return } - fmt.Fprintf(s.out, "%-40s : pc %x opcode %s\n", s.info(scope.Address(), pc), pc, op.String()) + fmt.Fprintf(s.out, "%-40s : pc %x opcode %s \t\t| stk[:%d] %s\n", s.info(scope.Address(), pc), pc, op.String(), len(top), stkInfo) } func (s *SourceMapTracer) OnFault(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 9f7240772437..502b9d0ba6f9 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -147,6 +147,10 @@ "initCodeHash": "0xaedf0d0b0e94a0c5e7d987331d2fdba84230f5704a6ca33677e70cde7051b17e", "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" }, + "src/cannon/MIPS64.sol": { + "initCodeHash": "0x47c2bdd1e6fbb4941caa20a2ba5c2a66de198a8c7b540a9d4a0d84dbe8d3bfea", + "sourceCodeHash": "0xdb7f8a92ed552a2720f5fe3c0a32e4069026f0b23933145493ead88403206814" + }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", "sourceCodeHash": "0x979d8595d925c70a123e72c062fa58c9ef94777c2e93b6bc3231d6679e2e9055" diff --git a/packages/contracts-bedrock/snapshots/abi/MIPS64.json b/packages/contracts-bedrock/snapshots/abi/MIPS64.json new file mode 100644 index 000000000000..fe6007b60a89 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/MIPS64.json @@ -0,0 +1,93 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPreimageOracle", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract IPreimageOracle", + "name": "oracle_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_stateData", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_proof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_localContext", + "type": "bytes32" + } + ], + "name": "step", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "InvalidExitedValue", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidMemoryProof", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPC", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRMWInstruction", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSecondMemoryProof", + "type": "error" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/MIPS64.json b/packages/contracts-bedrock/snapshots/storageLayout/MIPS64.json new file mode 100644 index 000000000000..0637a088a01e --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/MIPS64.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/cannon/MIPS64.sol b/packages/contracts-bedrock/src/cannon/MIPS64.sol new file mode 100644 index 000000000000..dcc2a6ec2190 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/MIPS64.sol @@ -0,0 +1,951 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol"; +import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; +import { MIPS64Syscalls as sys } from "src/cannon/libraries/MIPS64Syscalls.sol"; +import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; +import { MIPS64Instructions as ins } from "src/cannon/libraries/MIPS64Instructions.sol"; +import { MIPS64Arch as arch } from "src/cannon/libraries/MIPS64Arch.sol"; +import { VMStatuses } from "src/dispute/lib/Types.sol"; +import { + InvalidMemoryProof, InvalidRMWInstruction, InvalidSecondMemoryProof +} from "src/cannon/libraries/CannonErrors.sol"; + +/// @title MIPS64 +/// @notice The MIPS64 contract emulates a single MIPS instruction. +/// It differs from MIPS.sol in that it supports MIPS64 instructions and multi-tasking. +contract MIPS64 is ISemver { + /// @notice The thread context. + /// Total state size: 8 + 1 + 1 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 32 * 8 = 322 bytes + struct ThreadState { + // metadata + uint64 threadID; + uint8 exitCode; + bool exited; + // state + uint64 futexAddr; + uint64 futexVal; + uint64 futexTimeoutStep; + uint64 pc; + uint64 nextPC; + uint64 lo; + uint64 hi; + uint64[32] registers; + } + + uint32 internal constant PACKED_THREAD_STATE_SIZE = 322; + + uint8 internal constant LL_STATUS_NONE = 0; + uint8 internal constant LL_STATUS_ACTIVE_32_BIT = 0x1; + uint8 internal constant LL_STATUS_ACTIVE_64_BIT = 0x2; + + /// @notice Stores the VM state. + /// Total state size: 32 + 32 + 8 + 8 + 1 + 8 + 8 + 1 + 1 + 8 + 8 + 8 + 1 + 32 + 32 + 8 = 196 bytes + /// If nextPC != pc + 4, then the VM is executing a branch/jump delay slot. + struct State { + bytes32 memRoot; + bytes32 preimageKey; + uint64 preimageOffset; + uint64 heap; + uint8 llReservationStatus; + uint64 llAddress; + uint64 llOwnerThread; + uint8 exitCode; + bool exited; + uint64 step; + uint64 stepsSinceLastContextSwitch; + uint64 wakeup; + bool traverseRight; + bytes32 leftThreadStack; + bytes32 rightThreadStack; + uint64 nextThreadID; + } + + /// @notice The semantic version of the MIPS64 contract. + /// @custom:semver 1.0.0-beta.1 + string public constant version = "1.0.0-beta.1"; + + /// @notice The preimage oracle contract. + IPreimageOracle internal immutable ORACLE; + + // The offset of the start of proof calldata (_threadWitness.offset) in the step() function + uint256 internal constant THREAD_PROOF_OFFSET = 388; + + // The offset of the start of proof calldata (_memProof.offset) in the step() function + uint256 internal constant MEM_PROOF_OFFSET = THREAD_PROOF_OFFSET + PACKED_THREAD_STATE_SIZE + 32; + + // The empty thread root - keccak256(bytes32(0) ++ bytes32(0)) + bytes32 internal constant EMPTY_THREAD_ROOT = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + + // State memory offset allocated during step + uint256 internal constant STATE_MEM_OFFSET = 0x80; + + // ThreadState memory offset allocated during step + uint256 internal constant TC_MEM_OFFSET = 0x280; + + /// @param _oracle The address of the preimage oracle contract. + constructor(IPreimageOracle _oracle) { + ORACLE = _oracle; + } + + /// @notice Getter for the pre-image oracle contract. + /// @return oracle_ The IPreimageOracle contract. + function oracle() external view returns (IPreimageOracle oracle_) { + oracle_ = ORACLE; + } + + /// @notice Executes a single step of the multi-threaded vm. + /// Will revert if any required input state is missing. + /// @param _stateData The encoded state witness data. + /// @param _proof The encoded proof data: <, . + /// Contains the thread context witness and the memory proof data for leaves within the MIPS VM's + /// memory. + /// The thread context witness is a packed tuple of the thread context and the immediate inner root of + /// the current thread stack. + /// @param _localContext The local key context for the preimage oracle. Optional, can be set as a constant + /// if the caller only requires one set of local keys. + function step(bytes calldata _stateData, bytes calldata _proof, bytes32 _localContext) public returns (bytes32) { + unchecked { + State memory state; + ThreadState memory thread; + uint32 exited; + assembly { + if iszero(eq(state, STATE_MEM_OFFSET)) { + // expected state mem offset check + revert(0, 0) + } + if iszero(eq(thread, TC_MEM_OFFSET)) { + // expected thread mem offset check + revert(0, 0) + } + if iszero(eq(mload(0x40), shl(5, 63))) { + // 4 + 16 state slots + 43 thread slots = 63 expected memory check + revert(0, 0) + } + if iszero(eq(_stateData.offset, 132)) { + // 32*4+4=132 expected state data offset + revert(0, 0) + } + if iszero(eq(_proof.offset, THREAD_PROOF_OFFSET)) { + // _stateData.offset = 132 + // stateData.length = 196 + // 32-byte align padding = 28 + // _proof size prefix = 32 + // expected thread proof offset equals the sum of the above is 388 + revert(0, 0) + } + + function putField(callOffset, memOffset, size) -> callOffsetOut, memOffsetOut { + // calldata is packed, thus starting left-aligned, shift-right to pad and right-align + let w := shr(shl(3, sub(32, size)), calldataload(callOffset)) + mstore(memOffset, w) + callOffsetOut := add(callOffset, size) + memOffsetOut := add(memOffset, 32) + } + + // Unpack state from calldata into memory + let c := _stateData.offset // calldata offset + let m := STATE_MEM_OFFSET // mem offset + c, m := putField(c, m, 32) // memRoot + c, m := putField(c, m, 32) // preimageKey + c, m := putField(c, m, 8) // preimageOffset + c, m := putField(c, m, 8) // heap + c, m := putField(c, m, 1) // llReservationStatus + c, m := putField(c, m, 8) // llAddress + c, m := putField(c, m, 8) // llOwnerThread + c, m := putField(c, m, 1) // exitCode + c, m := putField(c, m, 1) // exited + exited := mload(sub(m, 32)) + c, m := putField(c, m, 8) // step + c, m := putField(c, m, 8) // stepsSinceLastContextSwitch + c, m := putField(c, m, 8) // wakeup + c, m := putField(c, m, 1) // traverseRight + c, m := putField(c, m, 32) // leftThreadStack + c, m := putField(c, m, 32) // rightThreadStack + c, m := putField(c, m, 8) // nextThreadID + } + st.assertExitedIsValid(exited); + + if (state.exited) { + // thread state is unchanged + return outputState(); + } + + if ( + (state.leftThreadStack == EMPTY_THREAD_ROOT && !state.traverseRight) + || (state.rightThreadStack == EMPTY_THREAD_ROOT && state.traverseRight) + ) { + revert("MIPS64: active thread stack is empty"); + } + + state.step += 1; + + setThreadStateFromCalldata(thread); + validateCalldataThreadWitness(state, thread); + + // Search for the first thread blocked by the wakeup call, if wakeup is set + // Don't allow regular execution until we resolved if we have woken up any thread. + if (state.wakeup != sys.FUTEX_EMPTY_ADDR) { + if (state.wakeup == thread.futexAddr) { + // completed wake traversal + // resume execution on woken up thread + state.wakeup = sys.FUTEX_EMPTY_ADDR; + return outputState(); + } else { + bool traversingRight = state.traverseRight; + bool changedDirections = preemptThread(state, thread); + if (traversingRight && changedDirections) { + // then we've completed wake traversal + // resume thread execution + state.wakeup = sys.FUTEX_EMPTY_ADDR; + } + return outputState(); + } + } + + if (thread.exited) { + popThread(state); + return outputState(); + } + + // check if thread is blocked on a futex + if (thread.futexAddr != sys.FUTEX_EMPTY_ADDR) { + // if set, then check futex + // check timeout first + if (state.step > thread.futexTimeoutStep) { + // timeout! Allow execution + return onWaitComplete(thread, true); + } else { + uint64 mem = MIPS64Memory.readMem( + state.memRoot, + thread.futexAddr & arch.ADDRESS_MASK, + MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1) + ); + if (thread.futexVal == mem) { + // still got expected value, continue sleeping, try next thread. + preemptThread(state, thread); + return outputState(); + } else { + // wake thread up, the value at its address changed! + // Userspace can turn thread back to sleep if it was too sporadic. + return onWaitComplete(thread, false); + } + } + } + + if (state.stepsSinceLastContextSwitch >= sys.SCHED_QUANTUM) { + preemptThread(state, thread); + return outputState(); + } + state.stepsSinceLastContextSwitch += 1; + + // instruction fetch + uint256 insnProofOffset = MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 0); + (uint32 insn, uint32 opcode, uint32 fun) = + ins.getInstructionDetails(thread.pc, state.memRoot, insnProofOffset); + + // Handle syscall separately + // syscall (can read and write) + if (opcode == 0 && fun == 0xC) { + return handleSyscall(_localContext); + } + + // Handle RMW (read-modify-write) ops + if (opcode == ins.OP_LOAD_LINKED || opcode == ins.OP_STORE_CONDITIONAL) { + return handleRMWOps(state, thread, insn, opcode); + } + if (opcode == ins.OP_LOAD_LINKED64 || opcode == ins.OP_STORE_CONDITIONAL64) { + return handleRMWOps(state, thread, insn, opcode); + } + + // Exec the rest of the step logic + st.CpuScalars memory cpu = getCpuScalars(thread); + ins.CoreStepLogicParams memory coreStepArgs = ins.CoreStepLogicParams({ + cpu: cpu, + registers: thread.registers, + memRoot: state.memRoot, + memProofOffset: MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1), + insn: insn, + opcode: opcode, + fun: fun + }); + bool memUpdated; + uint64 memAddr; + (state.memRoot, memUpdated, memAddr) = ins.execMipsCoreStepLogic(coreStepArgs); + setStateCpuScalars(thread, cpu); + updateCurrentThreadRoot(); + if (memUpdated) { + handleMemoryUpdate(state, memAddr); + } + + return outputState(); + } + } + + function handleMemoryUpdate(State memory _state, uint64 _memAddr) internal pure { + if (_memAddr == (arch.ADDRESS_MASK & _state.llAddress)) { + // Reserved address was modified, clear the reservation + clearLLMemoryReservation(_state); + } + } + + function clearLLMemoryReservation(State memory _state) internal pure { + _state.llReservationStatus = LL_STATUS_NONE; + _state.llAddress = 0; + _state.llOwnerThread = 0; + } + + function handleRMWOps( + State memory _state, + ThreadState memory _thread, + uint32 _insn, + uint32 _opcode + ) + internal + returns (bytes32) + { + unchecked { + uint64 base = _thread.registers[(_insn >> 21) & 0x1F]; + uint32 rtReg = (_insn >> 16) & 0x1F; + uint64 addr = base + ins.signExtendImmediate(_insn); + + // Determine some opcode-specific parameters + uint8 targetStatus = LL_STATUS_ACTIVE_32_BIT; + uint64 byteLength = 4; + if (_opcode == ins.OP_LOAD_LINKED64 || _opcode == ins.OP_STORE_CONDITIONAL64) { + // Use 64-bit params + targetStatus = LL_STATUS_ACTIVE_64_BIT; + byteLength = 8; + } + + uint64 retVal = 0; + uint64 threadId = _thread.threadID; + if (_opcode == ins.OP_LOAD_LINKED || _opcode == ins.OP_LOAD_LINKED64) { + retVal = loadSubWord(_state, addr, byteLength, true); + + _state.llReservationStatus = targetStatus; + _state.llAddress = addr; + _state.llOwnerThread = threadId; + } else if (_opcode == ins.OP_STORE_CONDITIONAL || _opcode == ins.OP_STORE_CONDITIONAL64) { + // Check if our memory reservation is still intact + if ( + _state.llReservationStatus == targetStatus && _state.llOwnerThread == threadId + && _state.llAddress == addr + ) { + // Complete atomic update: set memory and return 1 for success + clearLLMemoryReservation(_state); + + uint64 val = _thread.registers[rtReg]; + storeSubWord(_state, addr, byteLength, val); + + retVal = 1; + } else { + // Atomic update failed, return 0 for failure + retVal = 0; + } + } else { + revert InvalidRMWInstruction(); + } + + st.CpuScalars memory cpu = getCpuScalars(_thread); + ins.handleRd(cpu, _thread.registers, rtReg, retVal, true); + setStateCpuScalars(_thread, cpu); + updateCurrentThreadRoot(); + + return outputState(); + } + } + + /// @notice Loads a subword of byteLength size contained from memory based on the low-order bits of vaddr + /// @param _vaddr The virtual address of the the subword. + /// @param _byteLength The size of the subword. + /// @param _signExtend Whether to sign extend the selected subwrod. + function loadSubWord( + State memory _state, + uint64 _vaddr, + uint64 _byteLength, + bool _signExtend + ) + internal + pure + returns (uint64 val_) + { + uint64 effAddr = _vaddr & arch.ADDRESS_MASK; + uint256 memProofOffset = MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1); + uint64 mem = MIPS64Memory.readMem(_state.memRoot, effAddr, memProofOffset); + val_ = ins.selectSubWord(_vaddr, mem, _byteLength, _signExtend); + } + + /// @notice Stores a word that has been updated by the specified subword at bit positions determined by the virtual + /// address + /// @param _vaddr The virtual address of the subword. + /// @param _byteLength The size of the subword. + /// @param _value The subword that updates _memWord. + function storeSubWord(State memory _state, uint64 _vaddr, uint64 _byteLength, uint64 _value) internal pure { + uint64 effAddr = _vaddr & arch.ADDRESS_MASK; + uint256 memProofOffset = MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1); + uint64 mem = MIPS64Memory.readMem(_state.memRoot, effAddr, memProofOffset); + + uint64 newMemVal = ins.updateSubWord(_vaddr, mem, _byteLength, _value); + _state.memRoot = MIPS64Memory.writeMem(effAddr, memProofOffset, newMemVal); + } + + function handleSyscall(bytes32 _localContext) internal returns (bytes32 out_) { + unchecked { + // Load state from memory offsets to reduce stack pressure + State memory state; + ThreadState memory thread; + assembly { + state := STATE_MEM_OFFSET + thread := TC_MEM_OFFSET + } + + // Load the syscall numbers and args from the registers + (uint64 syscall_no, uint64 a0, uint64 a1, uint64 a2, uint64 a3) = sys.getSyscallArgs(thread.registers); + // Syscalls that are unimplemented but known return with v0=0 and v1=0 + uint64 v0 = 0; + uint64 v1 = 0; + + if (syscall_no == sys.SYS_MMAP) { + (v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap); + } else if (syscall_no == sys.SYS_BRK) { + // brk: Returns a fixed address for the program break at 0x40000000 + v0 = sys.PROGRAM_BREAK; + } else if (syscall_no == sys.SYS_CLONE) { + if (sys.VALID_SYS_CLONE_FLAGS != a0) { + state.exited = true; + state.exitCode = VMStatuses.PANIC.raw(); + return outputState(); + } + v0 = state.nextThreadID; + v1 = 0; + ThreadState memory newThread; + newThread.threadID = state.nextThreadID; + newThread.exitCode = 0; + newThread.exited = false; + newThread.futexAddr = sys.FUTEX_EMPTY_ADDR; + newThread.futexVal = 0; + newThread.futexTimeoutStep = 0; + newThread.pc = thread.nextPC; + newThread.nextPC = thread.nextPC + 4; + newThread.lo = thread.lo; + newThread.hi = thread.hi; + for (uint256 i; i < 32; i++) { + newThread.registers[i] = thread.registers[i]; + } + newThread.registers[29] = a1; // set stack pointer + // the child will perceive a 0 value as returned value instead, and no error + newThread.registers[2] = 0; + newThread.registers[7] = 0; + state.nextThreadID++; + + // Preempt this thread for the new one. But not before updating PCs + st.CpuScalars memory cpu0 = getCpuScalars(thread); + sys.handleSyscallUpdates(cpu0, thread.registers, v0, v1); + setStateCpuScalars(thread, cpu0); + updateCurrentThreadRoot(); + pushThread(state, newThread); + return outputState(); + } else if (syscall_no == sys.SYS_EXIT_GROUP) { + // exit group: Sets the Exited and ExitCode states to true and argument 0. + state.exited = true; + state.exitCode = uint8(a0); + updateCurrentThreadRoot(); + return outputState(); + } else if (syscall_no == sys.SYS_READ) { + sys.SysReadParams memory args = sys.SysReadParams({ + a0: a0, + a1: a1, + a2: a2, + preimageKey: state.preimageKey, + preimageOffset: state.preimageOffset, + localContext: _localContext, + oracle: ORACLE, + proofOffset: MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1), + memRoot: state.memRoot + }); + // Encapsulate execution to avoid stack-too-deep error + (v0, v1) = execSysRead(state, args); + } else if (syscall_no == sys.SYS_WRITE) { + (v0, v1, state.preimageKey, state.preimageOffset) = sys.handleSysWrite({ + _a0: a0, + _a1: a1, + _a2: a2, + _preimageKey: state.preimageKey, + _preimageOffset: state.preimageOffset, + _proofOffset: MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1), + _memRoot: state.memRoot + }); + } else if (syscall_no == sys.SYS_FCNTL) { + (v0, v1) = sys.handleSysFcntl(a0, a1); + } else if (syscall_no == sys.SYS_GETTID) { + v0 = thread.threadID; + v1 = 0; + } else if (syscall_no == sys.SYS_EXIT) { + thread.exited = true; + thread.exitCode = uint8(a0); + if (lastThreadRemaining(state)) { + state.exited = true; + state.exitCode = uint8(a0); + } + updateCurrentThreadRoot(); + return outputState(); + } else if (syscall_no == sys.SYS_FUTEX) { + // args: a0 = addr, a1 = op, a2 = val, a3 = timeout + uint64 effAddr = a0 & arch.ADDRESS_MASK; + if (a1 == sys.FUTEX_WAIT_PRIVATE) { + uint64 mem = MIPS64Memory.readMem( + state.memRoot, effAddr, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1) + ); + if (mem != a2) { + v0 = sys.SYS_ERROR_SIGNAL; + v1 = sys.EAGAIN; + } else { + thread.futexAddr = effAddr; + thread.futexVal = a2; + thread.futexTimeoutStep = a3 == 0 ? sys.FUTEX_NO_TIMEOUT : state.step + sys.FUTEX_TIMEOUT_STEPS; + // Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete` + updateCurrentThreadRoot(); + return outputState(); + } + } else if (a1 == sys.FUTEX_WAKE_PRIVATE) { + // Trigger thread traversal starting from the left stack until we find one waiting on the wakeup + // address + state.wakeup = effAddr; + // Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees. + // The woken up thread should indicate this in userspace. + v0 = 0; + v1 = 0; + st.CpuScalars memory cpu0 = getCpuScalars(thread); + sys.handleSyscallUpdates(cpu0, thread.registers, v0, v1); + setStateCpuScalars(thread, cpu0); + preemptThread(state, thread); + state.traverseRight = state.leftThreadStack == EMPTY_THREAD_ROOT; + return outputState(); + } else { + v0 = sys.SYS_ERROR_SIGNAL; + v1 = sys.EINVAL; + } + } else if (syscall_no == sys.SYS_SCHED_YIELD || syscall_no == sys.SYS_NANOSLEEP) { + v0 = 0; + v1 = 0; + st.CpuScalars memory cpu0 = getCpuScalars(thread); + sys.handleSyscallUpdates(cpu0, thread.registers, v0, v1); + setStateCpuScalars(thread, cpu0); + preemptThread(state, thread); + return outputState(); + } else if (syscall_no == sys.SYS_OPEN) { + v0 = sys.SYS_ERROR_SIGNAL; + v1 = sys.EBADF; + } else if (syscall_no == sys.SYS_CLOCKGETTIME) { + if (a0 == sys.CLOCK_GETTIME_REALTIME_FLAG || a0 == sys.CLOCK_GETTIME_MONOTONIC_FLAG) { + v0 = 0; + v1 = 0; + uint64 secs = 0; + uint64 nsecs = 0; + if (a0 == sys.CLOCK_GETTIME_MONOTONIC_FLAG) { + secs = uint64(state.step / sys.HZ); + nsecs = uint64((state.step % sys.HZ) * (1_000_000_000 / sys.HZ)); + } + uint64 effAddr = a1 & arch.ADDRESS_MASK; + // First verify the effAddr path + if ( + !MIPS64Memory.isValidProof( + state.memRoot, effAddr, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1) + ) + ) { + revert InvalidMemoryProof(); + } + // Recompute the new root after updating effAddr + state.memRoot = + MIPS64Memory.writeMem(effAddr, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1), secs); + handleMemoryUpdate(state, effAddr); + // Verify the second memory proof against the newly computed root + if ( + !MIPS64Memory.isValidProof( + state.memRoot, effAddr + 8, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 2) + ) + ) { + revert InvalidSecondMemoryProof(); + } + state.memRoot = + MIPS64Memory.writeMem(effAddr + 8, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 2), nsecs); + handleMemoryUpdate(state, effAddr + 8); + } else { + v0 = sys.SYS_ERROR_SIGNAL; + v1 = sys.EINVAL; + } + } else if (syscall_no == sys.SYS_GETPID) { + v0 = 0; + v1 = 0; + } else if (syscall_no == sys.SYS_MUNMAP) { + // ignored + } else if (syscall_no == sys.SYS_GETAFFINITY) { + // ignored + } else if (syscall_no == sys.SYS_MADVISE) { + // ignored + } else if (syscall_no == sys.SYS_RTSIGPROCMASK) { + // ignored + } else if (syscall_no == sys.SYS_SIGALTSTACK) { + // ignored + } else if (syscall_no == sys.SYS_RTSIGACTION) { + // ignored + } else if (syscall_no == sys.SYS_PRLIMIT64) { + // ignored + } else if (syscall_no == sys.SYS_CLOSE) { + // ignored + } else if (syscall_no == sys.SYS_PREAD64) { + // ignored + } else if (syscall_no == sys.SYS_FSTAT) { + // ignored + } else if (syscall_no == sys.SYS_OPENAT) { + // ignored + } else if (syscall_no == sys.SYS_READLINK) { + // ignored + } else if (syscall_no == sys.SYS_READLINKAT) { + // ignored + } else if (syscall_no == sys.SYS_IOCTL) { + // ignored + } else if (syscall_no == sys.SYS_EPOLLCREATE1) { + // ignored + } else if (syscall_no == sys.SYS_PIPE2) { + // ignored + } else if (syscall_no == sys.SYS_EPOLLCTL) { + // ignored + } else if (syscall_no == sys.SYS_EPOLLPWAIT) { + // ignored + } else if (syscall_no == sys.SYS_GETRANDOM) { + // ignored + } else if (syscall_no == sys.SYS_UNAME) { + // ignored + } else if (syscall_no == sys.SYS_GETUID) { + // ignored + } else if (syscall_no == sys.SYS_GETGID) { + // ignored + } else if (syscall_no == sys.SYS_MINCORE) { + // ignored + } else if (syscall_no == sys.SYS_TGKILL) { + // ignored + } else if (syscall_no == sys.SYS_SETITIMER) { + // ignored + } else if (syscall_no == sys.SYS_TIMERCREATE) { + // ignored + } else if (syscall_no == sys.SYS_TIMERSETTIME) { + // ignored + } else if (syscall_no == sys.SYS_TIMERDELETE) { + // ignored + } else if (syscall_no == sys.SYS_GETRLIMIT) { + // ignored + } else if (syscall_no == sys.SYS_LSEEK) { + // ignored + } else { + revert("MIPS64: unimplemented syscall"); + } + + st.CpuScalars memory cpu = getCpuScalars(thread); + sys.handleSyscallUpdates(cpu, thread.registers, v0, v1); + setStateCpuScalars(thread, cpu); + + updateCurrentThreadRoot(); + out_ = outputState(); + } + } + + function execSysRead( + State memory _state, + sys.SysReadParams memory _args + ) + internal + view + returns (uint64 v0_, uint64 v1_) + { + bool memUpdated; + uint64 memAddr; + (v0_, v1_, _state.preimageOffset, _state.memRoot, memUpdated, memAddr) = sys.handleSysRead(_args); + if (memUpdated) { + handleMemoryUpdate(_state, memAddr); + } + } + + /// @notice Computes the hash of the MIPS state. + /// @return out_ The hashed MIPS state. + function outputState() internal returns (bytes32 out_) { + uint32 exited; + assembly { + // copies 'size' bytes, right-aligned in word at 'from', to 'to', incl. trailing data + function copyMem(from, to, size) -> fromOut, toOut { + mstore(to, mload(add(from, sub(32, size)))) + fromOut := add(from, 32) + toOut := add(to, size) + } + + // From points to the MIPS State + let from := STATE_MEM_OFFSET + + // Copy to the free memory pointer + let start := mload(0x40) + let to := start + + // Copy state to free memory + from, to := copyMem(from, to, 32) // memRoot + from, to := copyMem(from, to, 32) // preimageKey + from, to := copyMem(from, to, 8) // preimageOffset + from, to := copyMem(from, to, 8) // heap + from, to := copyMem(from, to, 1) // llReservationStatus + from, to := copyMem(from, to, 8) // llAddress + from, to := copyMem(from, to, 8) // llOwnerThread + let exitCode := mload(from) + from, to := copyMem(from, to, 1) // exitCode + exited := mload(from) + from, to := copyMem(from, to, 1) // exited + from, to := copyMem(from, to, 8) // step + from, to := copyMem(from, to, 8) // stepsSinceLastContextSwitch + from, to := copyMem(from, to, 8) // wakeup + from, to := copyMem(from, to, 1) // traverseRight + from, to := copyMem(from, to, 32) // leftThreadStack + from, to := copyMem(from, to, 32) // rightThreadStack + from, to := copyMem(from, to, 8) // nextThreadID + + // Clean up end of memory + mstore(to, 0) + + // Log the resulting MIPS state, for debugging + log0(start, sub(to, start)) + + // Determine the VM status + let status := 0 + switch exited + case 1 { + switch exitCode + // VMStatusValid + case 0 { status := 0 } + // VMStatusInvalid + case 1 { status := 1 } + // VMStatusPanic + default { status := 2 } + } + // VMStatusUnfinished + default { status := 3 } + + // Compute the hash of the resulting MIPS state and set the status byte + out_ := keccak256(start, sub(to, start)) + out_ := or(and(not(shl(248, 0xFF)), out_), shl(248, status)) + } + + st.assertExitedIsValid(exited); + } + + /// @notice Updates the current thread stack root via inner thread root in calldata + function updateCurrentThreadRoot() internal pure { + State memory state; + ThreadState memory thread; + assembly { + state := STATE_MEM_OFFSET + thread := TC_MEM_OFFSET + } + bytes32 updatedRoot = computeThreadRoot(loadCalldataInnerThreadRoot(), thread); + if (state.traverseRight) { + state.rightThreadStack = updatedRoot; + } else { + state.leftThreadStack = updatedRoot; + } + } + + /// @notice Completes the FUTEX_WAIT syscall. + function onWaitComplete(ThreadState memory _thread, bool _isTimedOut) internal returns (bytes32 out_) { + // Note: no need to reset State.wakeup. If we're here, the wakeup field has already been reset + // Clear the futex state + _thread.futexAddr = sys.FUTEX_EMPTY_ADDR; + _thread.futexVal = 0; + _thread.futexTimeoutStep = 0; + + // Complete the FUTEX_WAIT syscall + uint64 v0 = _isTimedOut ? sys.SYS_ERROR_SIGNAL : 0; + // set errno + uint64 v1 = _isTimedOut ? sys.ETIMEDOUT : 0; + st.CpuScalars memory cpu = getCpuScalars(_thread); + sys.handleSyscallUpdates(cpu, _thread.registers, v0, v1); + setStateCpuScalars(_thread, cpu); + + updateCurrentThreadRoot(); + out_ = outputState(); + } + + /// @notice Preempts the current thread for another and updates the VM state. + /// It reads the inner thread root from calldata to update the current thread stack root. + function preemptThread( + State memory _state, + ThreadState memory _thread + ) + internal + pure + returns (bool changedDirections_) + { + // pop thread from the current stack and push to the other stack + if (_state.traverseRight) { + require(_state.rightThreadStack != EMPTY_THREAD_ROOT, "MIPS64: empty right thread stack"); + _state.rightThreadStack = loadCalldataInnerThreadRoot(); + _state.leftThreadStack = computeThreadRoot(_state.leftThreadStack, _thread); + } else { + require(_state.leftThreadStack != EMPTY_THREAD_ROOT, "MIPS64: empty left thread stack"); + _state.leftThreadStack = loadCalldataInnerThreadRoot(); + _state.rightThreadStack = computeThreadRoot(_state.rightThreadStack, _thread); + } + bytes32 current = _state.traverseRight ? _state.rightThreadStack : _state.leftThreadStack; + if (current == EMPTY_THREAD_ROOT) { + _state.traverseRight = !_state.traverseRight; + changedDirections_ = true; + } + _state.stepsSinceLastContextSwitch = 0; + } + + /// @notice Pushes a thread to the current thread stack. + function pushThread(State memory _state, ThreadState memory _thread) internal pure { + if (_state.traverseRight) { + _state.rightThreadStack = computeThreadRoot(_state.rightThreadStack, _thread); + } else { + _state.leftThreadStack = computeThreadRoot(_state.leftThreadStack, _thread); + } + _state.stepsSinceLastContextSwitch = 0; + } + + /// @notice Removes the current thread from the stack. + function popThread(State memory _state) internal pure { + if (_state.traverseRight) { + _state.rightThreadStack = loadCalldataInnerThreadRoot(); + } else { + _state.leftThreadStack = loadCalldataInnerThreadRoot(); + } + bytes32 current = _state.traverseRight ? _state.rightThreadStack : _state.leftThreadStack; + if (current == EMPTY_THREAD_ROOT) { + _state.traverseRight = !_state.traverseRight; + } + _state.stepsSinceLastContextSwitch = 0; + } + + /// @notice Returns true if the number of threads is 1 + function lastThreadRemaining(State memory _state) internal pure returns (bool out_) { + bytes32 inactiveStack = _state.traverseRight ? _state.leftThreadStack : _state.rightThreadStack; + bool currentStackIsAlmostEmpty = loadCalldataInnerThreadRoot() == EMPTY_THREAD_ROOT; + return inactiveStack == EMPTY_THREAD_ROOT && currentStackIsAlmostEmpty; + } + + function computeThreadRoot(bytes32 _currentRoot, ThreadState memory _thread) internal pure returns (bytes32 out_) { + // w_i = hash(w_0 ++ hash(thread)) + bytes32 threadRoot = outputThreadState(_thread); + out_ = keccak256(abi.encodePacked(_currentRoot, threadRoot)); + } + + function outputThreadState(ThreadState memory _thread) internal pure returns (bytes32 out_) { + assembly { + // copies 'size' bytes, right-aligned in word at 'from', to 'to', incl. trailing data + function copyMem(from, to, size) -> fromOut, toOut { + mstore(to, mload(add(from, sub(32, size)))) + fromOut := add(from, 32) + toOut := add(to, size) + } + + // From points to the ThreadState + let from := _thread + + // Copy to the free memory pointer + let start := mload(0x40) + let to := start + + // Copy state to free memory + from, to := copyMem(from, to, 8) // threadID + from, to := copyMem(from, to, 1) // exitCode + from, to := copyMem(from, to, 1) // exited + from, to := copyMem(from, to, 8) // futexAddr + from, to := copyMem(from, to, 8) // futexVal + from, to := copyMem(from, to, 8) // futexTimeoutStep + from, to := copyMem(from, to, 8) // pc + from, to := copyMem(from, to, 8) // nextPC + from, to := copyMem(from, to, 8) // lo + from, to := copyMem(from, to, 8) // hi + from := mload(from) // offset to registers + // Copy registers + for { let i := 0 } lt(i, 32) { i := add(i, 1) } { from, to := copyMem(from, to, 8) } + + // Clean up end of memory + mstore(to, 0) + + // Compute the hash of the resulting ThreadState + out_ := keccak256(start, sub(to, start)) + } + } + + function getCpuScalars(ThreadState memory _tc) internal pure returns (st.CpuScalars memory cpu_) { + cpu_ = st.CpuScalars({ pc: _tc.pc, nextPC: _tc.nextPC, lo: _tc.lo, hi: _tc.hi }); + } + + function setStateCpuScalars(ThreadState memory _tc, st.CpuScalars memory _cpu) internal pure { + _tc.pc = _cpu.pc; + _tc.nextPC = _cpu.nextPC; + _tc.lo = _cpu.lo; + _tc.hi = _cpu.hi; + } + + /// @notice Validates the thread witness in calldata against the current thread. + function validateCalldataThreadWitness(State memory _state, ThreadState memory _thread) internal pure { + bytes32 witnessRoot = computeThreadRoot(loadCalldataInnerThreadRoot(), _thread); + bytes32 expectedRoot = _state.traverseRight ? _state.rightThreadStack : _state.leftThreadStack; + require(expectedRoot == witnessRoot, "MIPS64: invalid thread witness"); + } + + /// @notice Sets the thread context from calldata. + function setThreadStateFromCalldata(ThreadState memory _thread) internal pure { + uint256 s = 0; + assembly { + s := calldatasize() + } + // verify we have enough calldata + require( + s >= (THREAD_PROOF_OFFSET + PACKED_THREAD_STATE_SIZE), "MIPS64: insufficient calldata for thread witness" + ); + + unchecked { + assembly { + function putField(callOffset, memOffset, size) -> callOffsetOut, memOffsetOut { + // calldata is packed, thus starting left-aligned, shift-right to pad and right-align + let w := shr(shl(3, sub(32, size)), calldataload(callOffset)) + mstore(memOffset, w) + callOffsetOut := add(callOffset, size) + memOffsetOut := add(memOffset, 32) + } + + let c := THREAD_PROOF_OFFSET + let m := _thread + c, m := putField(c, m, 8) // threadID + c, m := putField(c, m, 1) // exitCode + c, m := putField(c, m, 1) // exited + c, m := putField(c, m, 8) // futexAddr + c, m := putField(c, m, 8) // futexVal + c, m := putField(c, m, 8) // futexTimeoutStep + c, m := putField(c, m, 8) // pc + c, m := putField(c, m, 8) // nextPC + c, m := putField(c, m, 8) // lo + c, m := putField(c, m, 8) // hi + m := mload(m) // offset to registers + // Unpack register calldata into memory + for { let i := 0 } lt(i, 32) { i := add(i, 1) } { c, m := putField(c, m, 8) } + } + } + } + + /// @notice Loads the inner root for the current thread hash onion from calldata. + function loadCalldataInnerThreadRoot() internal pure returns (bytes32 innerThreadRoot_) { + uint256 s = 0; + assembly { + s := calldatasize() + innerThreadRoot_ := calldataload(add(THREAD_PROOF_OFFSET, PACKED_THREAD_STATE_SIZE)) + } + // verify we have enough calldata + require( + s >= (THREAD_PROOF_OFFSET + (PACKED_THREAD_STATE_SIZE + 32)), + "MIPS64: insufficient calldata for thread witness" + ); + } +} diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Arch.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Arch.sol new file mode 100644 index 000000000000..34a8d39f5d42 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Arch.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +library MIPS64Arch { + uint64 internal constant WORD_SIZE = 64; + uint64 internal constant WORD_SIZE_BYTES = 8; + uint64 internal constant EXT_MASK = 0x7; + uint64 internal constant ADDRESS_MASK = 0xFFFFFFFFFFFFFFF8; +} diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol new file mode 100644 index 000000000000..98bf87ec6239 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol @@ -0,0 +1,920 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; +import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; +import { MIPS64Arch as arch } from "src/cannon/libraries/MIPS64Arch.sol"; + +library MIPS64Instructions { + uint32 internal constant OP_LOAD_LINKED = 0x30; + uint32 internal constant OP_STORE_CONDITIONAL = 0x38; + uint32 internal constant OP_LOAD_LINKED64 = 0x34; + uint32 internal constant OP_STORE_CONDITIONAL64 = 0x3C; + uint32 internal constant OP_LOAD_DOUBLE_LEFT = 0x1A; + uint32 internal constant OP_LOAD_DOUBLE_RIGHT = 0x1B; + uint32 internal constant REG_RA = 31; + uint64 internal constant U64_MASK = 0xFFFFFFFFFFFFFFFF; + uint32 internal constant U32_MASK = 0xFFffFFff; + + error InvalidPC(); + + struct CoreStepLogicParams { + /// @param opcode The opcode value parsed from insn_. + st.CpuScalars cpu; + /// @param registers The CPU registers. + uint64[32] registers; + /// @param memRoot The current merkle root of the memory. + bytes32 memRoot; + /// @param memProofOffset The offset in calldata specify where the memory merkle proof is located. + uint256 memProofOffset; + /// @param insn The current 32-bit instruction at the pc. + uint32 insn; + /// @param cpu The CPU scalar fields. + uint32 opcode; + /// @param fun The function value parsed from insn_. + uint32 fun; + } + + /// @param _pc The program counter. + /// @param _memRoot The current memory root. + /// @param _insnProofOffset The calldata offset of the memory proof for the current instruction. + /// @return insn_ The current 32-bit instruction at the pc. + /// @return opcode_ The opcode value parsed from insn_. + /// @return fun_ The function value parsed from insn_. + function getInstructionDetails( + uint64 _pc, + bytes32 _memRoot, + uint256 _insnProofOffset + ) + internal + pure + returns (uint32 insn_, uint32 opcode_, uint32 fun_) + { + unchecked { + if (_pc & 0x3 != 0) { + revert InvalidPC(); + } + uint64 word = MIPS64Memory.readMem(_memRoot, _pc & arch.ADDRESS_MASK, _insnProofOffset); + insn_ = uint32(selectSubWord(_pc, word, 4, false)); + opcode_ = insn_ >> 26; // First 6-bits + fun_ = insn_ & 0x3f; // Last 6-bits + + return (insn_, opcode_, fun_); + } + } + + /// @notice Execute core MIPS step logic. + /// @return newMemRoot_ The updated merkle root of memory after any modifications, may be unchanged. + /// @return memUpdated_ True if memory was modified. + /// @return memAddr_ Holds the memory address that was updated if memUpdated_ is true. + function execMipsCoreStepLogic(CoreStepLogicParams memory _args) + internal + pure + returns (bytes32 newMemRoot_, bool memUpdated_, uint64 memAddr_) + { + unchecked { + newMemRoot_ = _args.memRoot; + memUpdated_ = false; + memAddr_ = 0; + + // j-type j/jal + if (_args.opcode == 2 || _args.opcode == 3) { + // Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset + uint64 target = (_args.cpu.nextPC & signExtend(0xF0000000, 32)) | uint64((_args.insn & 0x03FFFFFF) << 2); + handleJump(_args.cpu, _args.registers, _args.opcode == 2 ? 0 : REG_RA, target); + return (newMemRoot_, memUpdated_, memAddr_); + } + + // register fetch + uint64 rs = 0; // source register 1 value + uint64 rt = 0; // source register 2 / temp value + uint64 rtReg = uint64((_args.insn >> 16) & 0x1F); + + // R-type or I-type (stores rt) + rs = _args.registers[(_args.insn >> 21) & 0x1F]; + uint64 rdReg = rtReg; + + // 64-bit opcodes lwu, ldl, ldr + if (_args.opcode == 0x27 || _args.opcode == 0x1A || _args.opcode == 0x1B) { + rt = _args.registers[rtReg]; + rdReg = rtReg; + } else if (_args.opcode == 0 || _args.opcode == 0x1c) { + // R-type (stores rd) + rt = _args.registers[rtReg]; + rdReg = uint64((_args.insn >> 11) & 0x1F); + } else if (_args.opcode < 0x20) { + // rt is SignExtImm + // don't sign extend for andi, ori, xori + if (_args.opcode == 0xC || _args.opcode == 0xD || _args.opcode == 0xe) { + // ZeroExtImm + rt = uint64(_args.insn & 0xFFFF); + } else { + // SignExtImm + rt = signExtendImmediate(_args.insn); + } + } else if (_args.opcode >= 0x28 || _args.opcode == 0x22 || _args.opcode == 0x26) { + // store rt value with store + rt = _args.registers[rtReg]; + + // store actual rt with lwl and lwr + rdReg = rtReg; + } + + if ((_args.opcode >= 4 && _args.opcode < 8) || _args.opcode == 1) { + handleBranch({ + _cpu: _args.cpu, + _registers: _args.registers, + _opcode: _args.opcode, + _insn: _args.insn, + _rtReg: rtReg, + _rs: rs + }); + return (newMemRoot_, memUpdated_, memAddr_); + } + + uint64 storeAddr = U64_MASK; + // memory fetch (all I-type) + // we do the load for stores also + uint64 mem = 0; + if (_args.opcode >= 0x20 || _args.opcode == OP_LOAD_DOUBLE_LEFT || _args.opcode == OP_LOAD_DOUBLE_RIGHT) { + // M[R[rs]+SignExtImm] + rs += signExtendImmediate(_args.insn); + uint64 addr = rs & arch.ADDRESS_MASK; + mem = MIPS64Memory.readMem(_args.memRoot, addr, _args.memProofOffset); + if (_args.opcode >= 0x28) { + // store for 32-bit + // for 64-bit: ld (0x37) is the only non-store opcode >= 0x28 + if (_args.opcode != 0x37) { + // store + storeAddr = addr; + // store opcodes don't write back to a register + rdReg = 0; + } + } + } + + // ALU + // Note: swr outputs more than 8 bytes without the u64_mask + uint64 val = executeMipsInstruction(_args.insn, _args.opcode, _args.fun, rs, rt, mem) & U64_MASK; + + uint64 funSel = 0x20; + if (_args.opcode == 0 && _args.fun >= 8 && _args.fun < funSel) { + if (_args.fun == 8 || _args.fun == 9) { + // jr/jalr + handleJump(_args.cpu, _args.registers, _args.fun == 8 ? 0 : rdReg, rs); + return (newMemRoot_, memUpdated_, memAddr_); + } + + if (_args.fun == 0xa) { + // movz + handleRd(_args.cpu, _args.registers, rdReg, rs, rt == 0); + return (newMemRoot_, memUpdated_, memAddr_); + } + if (_args.fun == 0xb) { + // movn + handleRd(_args.cpu, _args.registers, rdReg, rs, rt != 0); + return (newMemRoot_, memUpdated_, memAddr_); + } + + // lo and hi registers + // can write back + if (_args.fun >= 0x10 && _args.fun < funSel) { + handleHiLo({ + _cpu: _args.cpu, + _registers: _args.registers, + _fun: _args.fun, + _rs: rs, + _rt: rt, + _storeReg: rdReg + }); + return (newMemRoot_, memUpdated_, memAddr_); + } + } + + // write memory + if (storeAddr != U64_MASK) { + newMemRoot_ = MIPS64Memory.writeMem(storeAddr, _args.memProofOffset, val); + memUpdated_ = true; + memAddr_ = storeAddr; + } + + // write back the value to destination register + handleRd(_args.cpu, _args.registers, rdReg, val, true); + + return (newMemRoot_, memUpdated_, memAddr_); + } + } + + function signExtendImmediate(uint32 _insn) internal pure returns (uint64 offset_) { + unchecked { + return signExtend(_insn & 0xFFFF, 16); + } + } + + /// @notice Execute an instruction. + function executeMipsInstruction( + uint32 _insn, + uint32 _opcode, + uint32 _fun, + uint64 _rs, + uint64 _rt, + uint64 _mem + ) + internal + pure + returns (uint64 out_) + { + unchecked { + if (_opcode == 0 || (_opcode >= 8 && _opcode < 0xF) || _opcode == 0x18 || _opcode == 0x19) { + assembly { + // transform ArithLogI to SPECIAL + switch _opcode + // addi + case 0x8 { _fun := 0x20 } + // addiu + case 0x9 { _fun := 0x21 } + // stli + case 0xA { _fun := 0x2A } + // sltiu + case 0xB { _fun := 0x2B } + // andi + case 0xC { _fun := 0x24 } + // ori + case 0xD { _fun := 0x25 } + // xori + case 0xE { _fun := 0x26 } + // daddi + case 0x18 { _fun := 0x2C } + // daddiu + case 0x19 { _fun := 0x2D } + } + + // sll + if (_fun == 0x00) { + return signExtend((_rt & U32_MASK) << ((_insn >> 6) & 0x1F), 32); + } + // srl + else if (_fun == 0x02) { + return signExtend((_rt & U32_MASK) >> ((_insn >> 6) & 0x1F), 32); + } + // sra + else if (_fun == 0x03) { + uint32 shamt = (_insn >> 6) & 0x1F; + return signExtend((_rt & U32_MASK) >> shamt, 32 - shamt); + } + // sllv + else if (_fun == 0x04) { + return signExtend((_rt & U32_MASK) << (_rs & 0x1F), 32); + } + // srlv + else if (_fun == 0x6) { + return signExtend((_rt & U32_MASK) >> (_rs & 0x1F), 32); + } + // srav + else if (_fun == 0x07) { + // shamt here is different than the typical shamt which comes from the + // instruction itself, here it comes from the rs register + uint64 shamt = _rs & 0x1F; + return signExtend((_rt & U32_MASK) >> shamt, 32 - shamt); + } + // functs in range [0x8, 0x1b] are handled specially by other functions + // Explicitly enumerate each funct in range to reduce code diff against Go Vm + // jr + else if (_fun == 0x08) { + return _rs; + } + // jalr + else if (_fun == 0x09) { + return _rs; + } + // movz + else if (_fun == 0x0a) { + return _rs; + } + // movn + else if (_fun == 0x0b) { + return _rs; + } + // syscall + else if (_fun == 0x0c) { + return _rs; + } + // 0x0d - break not supported + // sync + else if (_fun == 0x0f) { + return _rs; + } + // mfhi + else if (_fun == 0x10) { + return _rs; + } + // mthi + else if (_fun == 0x11) { + return _rs; + } + // mflo + else if (_fun == 0x12) { + return _rs; + } + // mtlo + else if (_fun == 0x13) { + return _rs; + } + // dsllv + else if (_fun == 0x14) { + return _rt; + } + // dsrlv + else if (_fun == 0x16) { + return _rt; + } + // dsrav + else if (_fun == 0x17) { + return _rt; + } + // mult + else if (_fun == 0x18) { + return _rs; + } + // multu + else if (_fun == 0x19) { + return _rs; + } + // div + else if (_fun == 0x1a) { + return _rs; + } + // divu + else if (_fun == 0x1b) { + return _rs; + } + // dmult + else if (_fun == 0x1c) { + return _rs; + } + // dmultu + else if (_fun == 0x1d) { + return _rs; + } + // ddiv + else if (_fun == 0x1e) { + return _rs; + } + // ddivu + else if (_fun == 0x1f) { + return _rs; + } + // The rest includes transformed R-type arith imm instructions + // add + else if (_fun == 0x20) { + return signExtend(uint64(uint32(_rs) + uint32(_rt)), 32); + } + // addu + else if (_fun == 0x21) { + return signExtend(uint64(uint32(_rs) + uint32(_rt)), 32); + } + // sub + else if (_fun == 0x22) { + return signExtend(uint64(uint32(_rs) - uint32(_rt)), 32); + } + // subu + else if (_fun == 0x23) { + return signExtend(uint64(uint32(_rs) - uint32(_rt)), 32); + } + // and + else if (_fun == 0x24) { + return (_rs & _rt); + } + // or + else if (_fun == 0x25) { + return (_rs | _rt); + } + // xor + else if (_fun == 0x26) { + return (_rs ^ _rt); + } + // nor + else if (_fun == 0x27) { + return ~(_rs | _rt); + } + // slti + else if (_fun == 0x2a) { + return int64(_rs) < int64(_rt) ? 1 : 0; + } + // sltiu + else if (_fun == 0x2b) { + return _rs < _rt ? 1 : 0; + } + // dadd + else if (_fun == 0x2c) { + return (_rs + _rt); + } + // daddu + else if (_fun == 0x2d) { + return (_rs + _rt); + } + // dsub + else if (_fun == 0x2e) { + return (_rs - _rt); + } + // dsubu + else if (_fun == 0x2f) { + return (_rs - _rt); + } + // dsll + else if (_fun == 0x38) { + return _rt << ((_insn >> 6) & 0x1f); + } + // dsrl + else if (_fun == 0x3A) { + return _rt >> ((_insn >> 6) & 0x1f); + } + // dsra + else if (_fun == 0x3B) { + return uint64(int64(_rt) >> ((_insn >> 6) & 0x1f)); + } + // dsll32 + else if (_fun == 0x3c) { + return _rt << (((_insn >> 6) & 0x1f) + 32); + } + // dsrl32 + else if (_fun == 0x3e) { + return _rt >> (((_insn >> 6) & 0x1f) + 32); + } + // dsra32 + else if (_fun == 0x3f) { + return uint64(int64(_rt) >> (((_insn >> 6) & 0x1f) + 32)); + } else { + revert("MIPS64: invalid instruction"); + } + } else { + // SPECIAL2 + if (_opcode == 0x1C) { + // mul + if (_fun == 0x2) { + return signExtend(uint32(int32(uint32(_rs)) * int32(uint32(_rt))), 32); + } + // clz, clo + else if (_fun == 0x20 || _fun == 0x21) { + if (_fun == 0x20) { + _rs = ~_rs; + } + uint32 i = 0; + while (_rs & 0x80000000 != 0) { + i++; + _rs <<= 1; + } + return i; + } + } + // lui + else if (_opcode == 0x0F) { + return signExtend(_rt << 16, 32); + } + // lb + else if (_opcode == 0x20) { + return selectSubWord(_rs, _mem, 1, true); + } + // lh + else if (_opcode == 0x21) { + return selectSubWord(_rs, _mem, 2, true); + } + // lwl + else if (_opcode == 0x22) { + uint32 w = uint32(selectSubWord(_rs, _mem, 4, false)); + uint32 val = w << uint32((_rs & 3) * 8); + uint64 mask = uint64(U32_MASK << uint32((_rs & 3) * 8)); + return signExtend(((_rt & ~mask) | uint64(val)) & U32_MASK, 32); + } + // lw + else if (_opcode == 0x23) { + return selectSubWord(_rs, _mem, 4, true); + } + // lbu + else if (_opcode == 0x24) { + return selectSubWord(_rs, _mem, 1, false); + } + // lhu + else if (_opcode == 0x25) { + return selectSubWord(_rs, _mem, 2, false); + } + // lwr + else if (_opcode == 0x26) { + uint32 w = uint32(selectSubWord(_rs, _mem, 4, false)); + uint32 val = w >> (24 - (_rs & 3) * 8); + uint32 mask = U32_MASK >> (24 - (_rs & 3) * 8); + uint64 lwrResult = (uint32(_rt) & ~mask) | val; + if (_rs & 3 == 3) { + // loaded bit 31 + return signExtend(uint64(lwrResult), 32); + } else { + // NOTE: cannon64 implementation specific: We leave the upper word untouched + uint64 rtMask = 0xFF_FF_FF_FF_00_00_00_00; + return ((_rt & rtMask) | uint64(lwrResult)); + } + } + // sb + else if (_opcode == 0x28) { + return updateSubWord(_rs, _mem, 1, _rt); + } + // sh + else if (_opcode == 0x29) { + return updateSubWord(_rs, _mem, 2, _rt); + } + // swl + else if (_opcode == 0x2a) { + uint64 sr = (_rs & 3) << 3; + uint64 val = ((_rt & U32_MASK) >> sr) << (32 - ((_rs & 0x4) << 3)); + uint64 mask = (uint64(U32_MASK) >> sr) << (32 - ((_rs & 0x4) << 3)); + return (_mem & ~mask) | val; + } + // sw + else if (_opcode == 0x2b) { + return updateSubWord(_rs, _mem, 4, _rt); + } + // swr + else if (_opcode == 0x2e) { + uint32 w = uint32(selectSubWord(_rs, _mem, 4, false)); + uint64 val = _rt << (24 - (_rs & 3) * 8); + uint64 mask = U32_MASK << uint32(24 - (_rs & 3) * 8); + uint64 swrResult = (w & ~mask) | uint32(val); + return updateSubWord(_rs, _mem, 4, swrResult); + } + // MIPS64 + // ldl + else if (_opcode == 0x1a) { + uint64 sl = (_rs & 0x7) << 3; + uint64 val = _mem << sl; + uint64 mask = U64_MASK << sl; + return (val | (_rt & ~mask)); + } + // ldr + else if (_opcode == 0x1b) { + uint64 sr = 56 - ((_rs & 0x7) << 3); + uint64 val = _mem >> sr; + uint64 mask = U64_MASK << (64 - sr); + return (val | (_rt & mask)); + } + // lwu + else if (_opcode == 0x27) { + return ((_mem >> (32 - ((_rs & 0x4) << 3))) & U32_MASK); + } + // sdl + else if (_opcode == 0x2c) { + uint64 sr = (_rs & 0x7) << 3; + uint64 val = _rt >> sr; + uint64 mask = U64_MASK >> sr; + return (val | (_mem & ~mask)); + } + // sdr + else if (_opcode == 0x2d) { + uint64 sl = 56 - ((_rs & 0x7) << 3); + uint64 val = _rt << sl; + uint64 mask = U64_MASK << sl; + return (val | (_mem & ~mask)); + } + // ld + else if (_opcode == 0x37) { + return _mem; + } + // sd + else if (_opcode == 0x3F) { + return _rt; + } else { + revert("MIPS64: invalid instruction"); + } + } + revert("MIPS64: invalid instruction"); + } + } + + /// @notice Extends the value leftwards with its most significant bit (sign extension). + function signExtend(uint64 _dat, uint64 _idx) internal pure returns (uint64 out_) { + unchecked { + bool isSigned = (_dat >> (_idx - 1)) != 0; + uint256 signed = ((1 << (arch.WORD_SIZE - _idx)) - 1) << _idx; + uint256 mask = (1 << _idx) - 1; + return uint64(_dat & mask | (isSigned ? signed : 0)); + } + } + + /// @notice Handles a branch instruction, updating the MIPS state PC where needed. + /// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo. + /// @param _registers Holds the current state of the cpu registers. + /// @param _opcode The opcode of the branch instruction. + /// @param _insn The instruction to be executed. + /// @param _rtReg The register to be used for the branch. + /// @param _rs The register to be compared with the branch register. + function handleBranch( + st.CpuScalars memory _cpu, + uint64[32] memory _registers, + uint32 _opcode, + uint32 _insn, + uint64 _rtReg, + uint64 _rs + ) + internal + pure + { + unchecked { + bool shouldBranch = false; + + if (_cpu.nextPC != _cpu.pc + 4) { + revert("MIPS64: branch in delay slot"); + } + + // beq/bne: Branch on equal / not equal + if (_opcode == 4 || _opcode == 5) { + uint64 rt = _registers[_rtReg]; + shouldBranch = (_rs == rt && _opcode == 4) || (_rs != rt && _opcode == 5); + } + // blez: Branches if instruction is less than or equal to zero + else if (_opcode == 6) { + shouldBranch = int64(_rs) <= 0; + } + // bgtz: Branches if instruction is greater than zero + else if (_opcode == 7) { + shouldBranch = int64(_rs) > 0; + } + // bltz/bgez: Branch on less than zero / greater than or equal to zero + else if (_opcode == 1) { + // regimm + uint32 rtv = ((_insn >> 16) & 0x1F); + if (rtv == 0) { + shouldBranch = int64(_rs) < 0; + } + if (rtv == 1) { + shouldBranch = int64(_rs) >= 0; + } + // bgezal (i.e. bal mnemonic) + if (rtv == 0x11) { + shouldBranch = int64(_rs) >= 0; + _registers[REG_RA] = _cpu.pc + 8; // always set regardless of branch taken + } + } + + // Update the state's previous PC + uint64 prevPC = _cpu.pc; + + // Execute the delay slot first + _cpu.pc = _cpu.nextPC; + + // If we should branch, update the PC to the branch target + // Otherwise, proceed to the next instruction + if (shouldBranch) { + _cpu.nextPC = prevPC + 4 + (signExtend(_insn & 0xFFFF, 16) << 2); + } else { + _cpu.nextPC = _cpu.nextPC + 4; + } + } + } + + /// @notice Handles HI and LO register instructions. It also additionally handles doubleword variable shift + /// operations + /// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo. + /// @param _registers Holds the current state of the cpu registers. + /// @param _fun The function code of the instruction. + /// @param _rs The value of the RS register. + /// @param _rt The value of the RT register. + /// @param _storeReg The register to store the result in. + function handleHiLo( + st.CpuScalars memory _cpu, + uint64[32] memory _registers, + uint32 _fun, + uint64 _rs, + uint64 _rt, + uint64 _storeReg + ) + internal + pure + { + unchecked { + uint64 val = 0; + + // mfhi: Move the contents of the HI register into the destination + if (_fun == 0x10) { + val = _cpu.hi; + } + // mthi: Move the contents of the source into the HI register + else if (_fun == 0x11) { + _cpu.hi = _rs; + } + // mflo: Move the contents of the LO register into the destination + else if (_fun == 0x12) { + val = _cpu.lo; + } + // mtlo: Move the contents of the source into the LO register + else if (_fun == 0x13) { + _cpu.lo = _rs; + } + // mult: Multiplies `rs` by `rt` and stores the result in HI and LO registers + else if (_fun == 0x18) { + uint64 acc = uint64(int64(int32(uint32(_rs))) * int64(int32(uint32(_rt)))); + _cpu.hi = signExtend(uint64(acc >> 32), 32); + _cpu.lo = signExtend(uint64(uint32(acc)), 32); + } + // multu: Unsigned multiplies `rs` by `rt` and stores the result in HI and LO registers + else if (_fun == 0x19) { + uint64 acc = uint64(uint32(_rs)) * uint64(uint32(_rt)); + _cpu.hi = signExtend(uint64(acc >> 32), 32); + _cpu.lo = signExtend(uint64(uint32(acc)), 32); + } + // div: Divides `rs` by `rt`. + // Stores the quotient in LO + // And the remainder in HI + else if (_fun == 0x1a) { + if (uint32(_rt) == 0) { + revert("MIPS64: division by zero"); + } + _cpu.hi = signExtend(uint32(int32(uint32(_rs)) % int32(uint32(_rt))), 32); + _cpu.lo = signExtend(uint32(int32(uint32(_rs)) / int32(uint32(_rt))), 32); + } + // divu: Unsigned divides `rs` by `rt`. + // Stores the quotient in LO + // And the remainder in HI + else if (_fun == 0x1b) { + if (uint32(_rt) == 0) { + revert("MIPS64: division by zero"); + } + _cpu.hi = signExtend(uint64(uint32(_rs) % uint32(_rt)), 32); + _cpu.lo = signExtend(uint64(uint32(_rs) / uint32(_rt)), 32); + } + // dsllv + else if (_fun == 0x14) { + val = _rt << (_rs & 0x3F); + } + // dsrlv + else if (_fun == 0x16) { + val = _rt >> (_rs & 0x3F); + } + // dsrav + else if (_fun == 0x17) { + val = uint64(int64(_rt) >> (_rs & 0x3F)); + } + // dmult + else if (_fun == 0x1c) { + int128 res = int128(int64(_rs)) * int128(int64(_rt)); + _cpu.hi = uint64(int64(res >> 64)); + _cpu.lo = uint64(uint128(res) & U64_MASK); + } + // dmultu + else if (_fun == 0x1d) { + uint128 res = uint128(_rs) * uint128(_rt); + _cpu.hi = uint64(res >> 64); + _cpu.lo = uint64(res); + } + // ddiv + else if (_fun == 0x1e) { + if (_rt == 0) { + revert("MIPS64: division by zero"); + } + _cpu.hi = uint64(int64(_rs) % int64(_rt)); + _cpu.lo = uint64(int64(_rs) / int64(_rt)); + } + // ddivu + else if (_fun == 0x1f) { + if (_rt == 0) { + revert("MIPS64: division by zero"); + } + _cpu.hi = _rs % _rt; + _cpu.lo = _rs / _rt; + } + + // Store the result in the destination register, if applicable + if (_storeReg != 0) { + _registers[_storeReg] = val; + } + + // Update the PC + _cpu.pc = _cpu.nextPC; + _cpu.nextPC = _cpu.nextPC + 4; + } + } + + /// @notice Handles a jump instruction, updating the MIPS state PC where needed. + /// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo. + /// @param _registers Holds the current state of the cpu registers. + /// @param _linkReg The register to store the link to the instruction after the delay slot instruction. + /// @param _dest The destination to jump to. + function handleJump( + st.CpuScalars memory _cpu, + uint64[32] memory _registers, + uint64 _linkReg, + uint64 _dest + ) + internal + pure + { + unchecked { + if (_cpu.nextPC != _cpu.pc + 4) { + revert("MIPS64: jump in delay slot"); + } + + // Update the next PC to the jump destination. + uint64 prevPC = _cpu.pc; + _cpu.pc = _cpu.nextPC; + _cpu.nextPC = _dest; + + // Update the link-register to the instruction after the delay slot instruction. + if (_linkReg != 0) { + _registers[_linkReg] = prevPC + 8; + } + } + } + + /// @notice Handles a storing a value into a register. + /// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo. + /// @param _registers Holds the current state of the cpu registers. + /// @param _storeReg The register to store the value into. + /// @param _val The value to store. + /// @param _conditional Whether or not the store is conditional. + function handleRd( + st.CpuScalars memory _cpu, + uint64[32] memory _registers, + uint64 _storeReg, + uint64 _val, + bool _conditional + ) + internal + pure + { + unchecked { + // The destination register must be valid. + require(_storeReg < 32, "MIPS64: valid register"); + + // Never write to reg 0, and it can be conditional (movz, movn). + if (_storeReg != 0 && _conditional) { + _registers[_storeReg] = _val; + } + + // Update the PC. + _cpu.pc = _cpu.nextPC; + _cpu.nextPC = _cpu.nextPC + 4; + } + } + + /// @notice Selects a subword of byteLength size contained in memWord based on the low-order bits of vaddr + /// @param _vaddr The virtual address of the the subword. + /// @param _memWord The full word to select a subword from. + /// @param _byteLength The size of the subword. + /// @param _signExtend Whether to sign extend the selected subwrod. + function selectSubWord( + uint64 _vaddr, + uint64 _memWord, + uint64 _byteLength, + bool _signExtend + ) + internal + pure + returns (uint64 retval_) + { + (uint64 dataMask, uint64 bitOffset, uint64 bitLength) = calculateSubWordMaskAndOffset(_vaddr, _byteLength); + retval_ = (_memWord >> bitOffset) & dataMask; + if (_signExtend) { + retval_ = signExtend(retval_, bitLength); + } + return retval_; + } + + /// @notice Returns a word that has been updated by the specified subword at bit positions determined by the virtual + /// address + /// @param _vaddr The virtual address of the subword. + /// @param _memWord The full word to update. + /// @param _byteLength The size of the subword. + /// @param _value The subword that updates _memWord. + function updateSubWord( + uint64 _vaddr, + uint64 _memWord, + uint64 _byteLength, + uint64 _value + ) + internal + pure + returns (uint64 word_) + { + (uint64 dataMask, uint64 bitOffset,) = calculateSubWordMaskAndOffset(_vaddr, _byteLength); + uint64 subWordValue = dataMask & _value; + uint64 memUpdateMask = dataMask << bitOffset; + return subWordValue << bitOffset | (~memUpdateMask) & _memWord; + } + + function calculateSubWordMaskAndOffset( + uint64 _vaddr, + uint64 _byteLength + ) + internal + pure + returns (uint64 dataMask_, uint64 bitOffset_, uint64 bitLength_) + { + uint64 bitLength = _byteLength << 3; + uint64 dataMask = ~uint64(0) >> (arch.WORD_SIZE - bitLength); + + // Figure out sub-word index based on the low-order bits in vaddr + uint64 byteIndexMask = _vaddr & arch.EXT_MASK & ~(_byteLength - 1); + uint64 maxByteShift = arch.WORD_SIZE_BYTES - _byteLength; + uint64 byteIndex = _vaddr & byteIndexMask; + uint64 bitOffset = (maxByteShift - byteIndex) << 3; + + return (dataMask, bitOffset, bitLength); + } +} diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol new file mode 100644 index 000000000000..09678148f3e6 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol"; + +library MIPS64State { + struct CpuScalars { + uint64 pc; + uint64 nextPC; + uint64 lo; + uint64 hi; + } + + function assertExitedIsValid(uint32 _exited) internal pure { + if (_exited > 1) { + revert InvalidExitedValue(); + } + } +} diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol new file mode 100644 index 000000000000..d03f04f08620 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; +import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; +import { PreimageKeyLib } from "src/cannon/PreimageKeyLib.sol"; +import { MIPS64Arch as arch } from "src/cannon/libraries/MIPS64Arch.sol"; + +library MIPS64Syscalls { + struct SysReadParams { + /// @param _a0 The file descriptor. + uint64 a0; + /// @param _a1 The memory location where data should be read to. + uint64 a1; + /// @param _a2 The number of bytes to read from the file + uint64 a2; + /// @param _preimageKey The key of the preimage to read. + bytes32 preimageKey; + /// @param _preimageOffset The offset of the preimage to read. + uint64 preimageOffset; + /// @param _localContext The local context for the preimage key. + bytes32 localContext; + /// @param _oracle The address of the preimage oracle. + IPreimageOracle oracle; + /// @param _proofOffset The offset of the memory proof in calldata. + uint256 proofOffset; + /// @param _memRoot The current memory root. + bytes32 memRoot; + } + + uint64 internal constant U64_MASK = 0xFFffFFffFFffFFff; + uint64 internal constant PAGE_ADDR_MASK = 4095; + uint64 internal constant PAGE_SIZE = 4096; + + uint32 internal constant SYS_MMAP = 5009; + uint32 internal constant SYS_BRK = 5012; + uint32 internal constant SYS_CLONE = 5055; + uint32 internal constant SYS_EXIT_GROUP = 5205; + uint32 internal constant SYS_READ = 5000; + uint32 internal constant SYS_WRITE = 5001; + uint32 internal constant SYS_FCNTL = 5070; + uint32 internal constant SYS_EXIT = 5058; + uint32 internal constant SYS_SCHED_YIELD = 5023; + uint32 internal constant SYS_GETTID = 5178; + uint32 internal constant SYS_FUTEX = 5194; + uint32 internal constant SYS_OPEN = 5002; + uint32 internal constant SYS_NANOSLEEP = 5034; + uint32 internal constant SYS_CLOCKGETTIME = 5222; + uint32 internal constant SYS_GETPID = 5038; + // no-op syscalls + uint32 internal constant SYS_MUNMAP = 5011; + uint32 internal constant SYS_GETAFFINITY = 5196; + uint32 internal constant SYS_MADVISE = 5027; + uint32 internal constant SYS_RTSIGPROCMASK = 5014; + uint32 internal constant SYS_SIGALTSTACK = 5129; + uint32 internal constant SYS_RTSIGACTION = 5013; + uint32 internal constant SYS_PRLIMIT64 = 5297; + uint32 internal constant SYS_CLOSE = 5003; + uint32 internal constant SYS_PREAD64 = 5016; + uint32 internal constant SYS_FSTAT = 5005; + //uint32 internal constant SYS_FSTAT64 = 0xFFFFFFFF; // UndefinedSysNr - not supported by MIPS64 + uint32 internal constant SYS_OPENAT = 5247; + uint32 internal constant SYS_READLINK = 5087; + uint32 internal constant SYS_READLINKAT = 5257; + uint32 internal constant SYS_IOCTL = 5015; + uint32 internal constant SYS_EPOLLCREATE1 = 5285; + uint32 internal constant SYS_PIPE2 = 5287; + uint32 internal constant SYS_EPOLLCTL = 5208; + uint32 internal constant SYS_EPOLLPWAIT = 5272; + uint32 internal constant SYS_GETRANDOM = 5313; + uint32 internal constant SYS_UNAME = 5061; + //uint32 internal constant SYS_STAT64 = 0xFFFFFFFF; // UndefinedSysNr - not supported by MIPS64 + uint32 internal constant SYS_GETUID = 5100; + uint32 internal constant SYS_GETGID = 5102; + //uint32 internal constant SYS_LLSEEK = 0xFFFFFFFF; // UndefinedSysNr - not supported by MIPS64 + uint32 internal constant SYS_MINCORE = 5026; + uint32 internal constant SYS_TGKILL = 5225; + uint32 internal constant SYS_GETRLIMIT = 5095; + uint32 internal constant SYS_LSEEK = 5008; + // profiling-related syscalls - ignored + uint32 internal constant SYS_SETITIMER = 5036; + uint32 internal constant SYS_TIMERCREATE = 5216; + uint32 internal constant SYS_TIMERSETTIME = 5217; + uint32 internal constant SYS_TIMERDELETE = 5220; + + uint32 internal constant FD_STDIN = 0; + uint32 internal constant FD_STDOUT = 1; + uint32 internal constant FD_STDERR = 2; + uint32 internal constant FD_HINT_READ = 3; + uint32 internal constant FD_HINT_WRITE = 4; + uint32 internal constant FD_PREIMAGE_READ = 5; + uint32 internal constant FD_PREIMAGE_WRITE = 6; + + uint64 internal constant SYS_ERROR_SIGNAL = U64_MASK; + uint64 internal constant EBADF = 0x9; + uint64 internal constant EINVAL = 0x16; + uint64 internal constant EAGAIN = 0xb; + uint64 internal constant ETIMEDOUT = 0x91; + + uint64 internal constant FUTEX_WAIT_PRIVATE = 128; + uint64 internal constant FUTEX_WAKE_PRIVATE = 129; + uint64 internal constant FUTEX_TIMEOUT_STEPS = 10000; + uint64 internal constant FUTEX_NO_TIMEOUT = type(uint64).max; + uint64 internal constant FUTEX_EMPTY_ADDR = U64_MASK; + + uint64 internal constant SCHED_QUANTUM = 100_000; + uint64 internal constant HZ = 10_000_000; + uint64 internal constant CLOCK_GETTIME_REALTIME_FLAG = 0; + uint64 internal constant CLOCK_GETTIME_MONOTONIC_FLAG = 1; + /// @notice Start of the data segment. + uint64 internal constant PROGRAM_BREAK = 0x00_00_40_00_00_00_00_00; + uint64 internal constant HEAP_END = 0x00_00_60_00_00_00_00_00; + + // SYS_CLONE flags + uint64 internal constant CLONE_VM = 0x100; + uint64 internal constant CLONE_FS = 0x200; + uint64 internal constant CLONE_FILES = 0x400; + uint64 internal constant CLONE_SIGHAND = 0x800; + uint64 internal constant CLONE_PTRACE = 0x2000; + uint64 internal constant CLONE_VFORK = 0x4000; + uint64 internal constant CLONE_PARENT = 0x8000; + uint64 internal constant CLONE_THREAD = 0x10000; + uint64 internal constant CLONE_NEWNS = 0x20000; + uint64 internal constant CLONE_SYSVSEM = 0x40000; + uint64 internal constant CLONE_SETTLS = 0x80000; + uint64 internal constant CLONE_PARENTSETTID = 0x100000; + uint64 internal constant CLONE_CHILDCLEARTID = 0x200000; + uint64 internal constant CLONE_UNTRACED = 0x800000; + uint64 internal constant CLONE_CHILDSETTID = 0x1000000; + uint64 internal constant CLONE_STOPPED = 0x2000000; + uint64 internal constant CLONE_NEWUTS = 0x4000000; + uint64 internal constant CLONE_NEWIPC = 0x8000000; + uint64 internal constant VALID_SYS_CLONE_FLAGS = + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_THREAD; + + // FYI: https://en.wikibooks.org/wiki/MIPS_Assembly/Register_File + // https://refspecs.linuxfoundation.org/elf/mipsabi.pdf + uint32 internal constant REG_V0 = 2; + uint32 internal constant REG_A0 = 4; + uint32 internal constant REG_A1 = 5; + uint32 internal constant REG_A2 = 6; + uint32 internal constant REG_A3 = 7; + + // FYI: https://web.archive.org/web/20231223163047/https://www.linux-mips.org/wiki/Syscall + uint32 internal constant REG_SYSCALL_NUM = REG_V0; + uint32 internal constant REG_SYSCALL_ERRNO = REG_A3; + uint32 internal constant REG_SYSCALL_RET1 = REG_V0; + uint32 internal constant REG_SYSCALL_PARAM1 = REG_A0; + uint32 internal constant REG_SYSCALL_PARAM2 = REG_A1; + uint32 internal constant REG_SYSCALL_PARAM3 = REG_A2; + uint32 internal constant REG_SYSCALL_PARAM4 = REG_A3; + + // Constants copied from MIPS64Arch for use in Yul + uint64 internal constant WORD_SIZE_BYTES = 8; + uint64 internal constant EXT_MASK = 0x7; + + /// @notice Extract syscall num and arguments from registers. + /// @param _registers The cpu registers. + /// @return sysCallNum_ The syscall number. + /// @return a0_ The first argument available to the syscall operation. + /// @return a1_ The second argument available to the syscall operation. + /// @return a2_ The third argument available to the syscall operation. + /// @return a3_ The fourth argument available to the syscall operation. + function getSyscallArgs(uint64[32] memory _registers) + internal + pure + returns (uint64 sysCallNum_, uint64 a0_, uint64 a1_, uint64 a2_, uint64 a3_) + { + unchecked { + sysCallNum_ = _registers[REG_SYSCALL_NUM]; + + a0_ = _registers[REG_SYSCALL_PARAM1]; + a1_ = _registers[REG_SYSCALL_PARAM2]; + a2_ = _registers[REG_SYSCALL_PARAM3]; + a3_ = _registers[REG_SYSCALL_PARAM4]; + + return (sysCallNum_, a0_, a1_, a2_, a3_); + } + } + + /// @notice Like a Linux mmap syscall. Allocates a page from the heap. + /// @param _a0 The address for the new mapping + /// @param _a1 The size of the new mapping + /// @param _heap The current value of the heap pointer + /// @return v0_ The address of the new mapping + /// @return v1_ Unused error code (0) + /// @return newHeap_ The new value for the heap, may be unchanged + function handleSysMmap( + uint64 _a0, + uint64 _a1, + uint64 _heap + ) + internal + pure + returns (uint64 v0_, uint64 v1_, uint64 newHeap_) + { + unchecked { + v1_ = uint64(0); + newHeap_ = _heap; + + uint64 sz = _a1; + if (sz & PAGE_ADDR_MASK != 0) { + // adjust size to align with page size + sz += PAGE_SIZE - (sz & PAGE_ADDR_MASK); + } + if (_a0 == 0) { + v0_ = _heap; + newHeap_ += sz; + // Fail if new heap exceeds memory limit, newHeap overflows to low memory, or sz overflows + if (newHeap_ > HEAP_END || newHeap_ < _heap || sz < _a1) { + v0_ = SYS_ERROR_SIGNAL; + v1_ = EINVAL; + return (v0_, v1_, _heap); + } + } else { + v0_ = _a0; + } + + return (v0_, v1_, newHeap_); + } + } + + /// @notice Like a Linux read syscall. Splits unaligned reads into aligned reads. + /// Args are provided as a struct to reduce stack pressure. + /// @return v0_ The number of bytes read, -1 on error. + /// @return v1_ The error code, 0 if there is no error. + /// @return newPreimageOffset_ The new value for the preimage offset. + /// @return newMemRoot_ The new memory root. + function handleSysRead(SysReadParams memory _args) + internal + view + returns ( + uint64 v0_, + uint64 v1_, + uint64 newPreimageOffset_, + bytes32 newMemRoot_, + bool memUpdated_, + uint64 memAddr_ + ) + { + unchecked { + v0_ = uint64(0); + v1_ = uint64(0); + newMemRoot_ = _args.memRoot; + newPreimageOffset_ = _args.preimageOffset; + memUpdated_ = false; + memAddr_ = 0; + + // args: _a0 = fd, _a1 = addr, _a2 = count + // returns: v0_ = read, v1_ = err code + if (_args.a0 == FD_STDIN) { + // Leave v0_ and v1_ zero: read nothing, no error + } + // pre-image oracle read + else if (_args.a0 == FD_PREIMAGE_READ) { + uint64 effAddr = _args.a1 & arch.ADDRESS_MASK; + // verify proof is correct, and get the existing memory. + // mask the addr to align it to 4 bytes + uint64 mem = MIPS64Memory.readMem(_args.memRoot, effAddr, _args.proofOffset); + // If the preimage key is a local key, localize it in the context of the caller. + if (uint8(_args.preimageKey[0]) == 1) { + _args.preimageKey = PreimageKeyLib.localize(_args.preimageKey, _args.localContext); + } + (bytes32 dat, uint256 datLen) = _args.oracle.readPreimage(_args.preimageKey, _args.preimageOffset); + + // Transform data for writing to memory + // We use assembly for more precise ops, and no var count limit + uint64 a1 = _args.a1; + uint64 a2 = _args.a2; + assembly { + let alignment := and(a1, EXT_MASK) // the read might not start at an aligned address + let space := sub(WORD_SIZE_BYTES, alignment) // remaining space in memory word + if lt(space, datLen) { datLen := space } // if less space than data, shorten data + if lt(a2, datLen) { datLen := a2 } // if requested to read less, read less + dat := shr(sub(256, mul(datLen, 8)), dat) // right-align data + // position data to insert into memory word + dat := shl(mul(sub(sub(WORD_SIZE_BYTES, datLen), alignment), 8), dat) + // mask all bytes after start + let mask := sub(shl(mul(sub(WORD_SIZE_BYTES, alignment), 8), 1), 1) + // mask of all bytes + let suffixMask := sub(shl(mul(sub(sub(WORD_SIZE_BYTES, alignment), datLen), 8), 1), 1) + // starting from end, maybe none + mask := and(mask, not(suffixMask)) // reduce mask to just cover the data we insert + mem := or(and(mem, not(mask)), dat) // clear masked part of original memory, and insert data + } + + // Write memory back + newMemRoot_ = MIPS64Memory.writeMem(effAddr, _args.proofOffset, mem); + memUpdated_ = true; + memAddr_ = effAddr; + newPreimageOffset_ += uint64(datLen); + v0_ = uint64(datLen); + } + // hint response + else if (_args.a0 == FD_HINT_READ) { + // Don't read into memory, just say we read it all + // The result is ignored anyway + v0_ = _args.a2; + } else { + v0_ = U64_MASK; + v1_ = EBADF; + } + + return (v0_, v1_, newPreimageOffset_, newMemRoot_, memUpdated_, memAddr_); + } + } + + /// @notice Like a Linux write syscall. Splits unaligned writes into aligned writes. + /// @param _a0 The file descriptor. + /// @param _a1 The memory address to read from. + /// @param _a2 The number of bytes to read. + /// @param _preimageKey The current preimaageKey. + /// @param _preimageOffset The current preimageOffset. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @param _memRoot The current memory root. + /// @return v0_ The number of bytes written, or -1 on error. + /// @return v1_ The error code, or 0 if empty. + /// @return newPreimageKey_ The new preimageKey. + /// @return newPreimageOffset_ The new preimageOffset. + function handleSysWrite( + uint64 _a0, + uint64 _a1, + uint64 _a2, + bytes32 _preimageKey, + uint64 _preimageOffset, + uint256 _proofOffset, + bytes32 _memRoot + ) + internal + pure + returns (uint64 v0_, uint64 v1_, bytes32 newPreimageKey_, uint64 newPreimageOffset_) + { + unchecked { + // args: _a0 = fd, _a1 = addr, _a2 = count + // returns: v0_ = written, v1_ = err code + v0_ = uint64(0); + v1_ = uint64(0); + newPreimageKey_ = _preimageKey; + newPreimageOffset_ = _preimageOffset; + + if (_a0 == FD_STDOUT || _a0 == FD_STDERR || _a0 == FD_HINT_WRITE) { + v0_ = _a2; // tell program we have written everything + } + // pre-image oracle + else if (_a0 == FD_PREIMAGE_WRITE) { + // mask the addr to align it to 4 bytes + uint64 mem = MIPS64Memory.readMem(_memRoot, _a1 & arch.ADDRESS_MASK, _proofOffset); + bytes32 key = _preimageKey; + + // Construct pre-image key from memory + // We use assembly for more precise ops, and no var count limit + assembly { + let alignment := and(_a1, EXT_MASK) // the read might not start at an aligned address + let space := sub(WORD_SIZE_BYTES, alignment) // remaining space in memory word + if lt(space, _a2) { _a2 := space } // if less space than data, shorten data + key := shl(mul(_a2, 8), key) // shift key, make space for new info + let mask := sub(shl(mul(_a2, 8), 1), 1) // mask for extracting value from memory + mem := and(shr(mul(sub(space, _a2), 8), mem), mask) // align value to right, mask it + key := or(key, mem) // insert into key + } + + // Write pre-image key to oracle + newPreimageKey_ = key; + newPreimageOffset_ = 0; // reset offset, to read new pre-image data from the start + v0_ = _a2; + } else { + v0_ = U64_MASK; + v1_ = EBADF; + } + + return (v0_, v1_, newPreimageKey_, newPreimageOffset_); + } + } + + /// @notice Like Linux fcntl (file control) syscall, but only supports minimal file-descriptor control commands, to + /// retrieve the file-descriptor R/W flags. + /// @param _a0 The file descriptor. + /// @param _a1 The control command. + /// @param v0_ The file status flag (only supported commands are F_GETFD and F_GETFL), or -1 on error. + /// @param v1_ An error number, or 0 if there is no error. + function handleSysFcntl(uint64 _a0, uint64 _a1) internal pure returns (uint64 v0_, uint64 v1_) { + unchecked { + v0_ = uint64(0); + v1_ = uint64(0); + + // args: _a0 = fd, _a1 = cmd + if (_a1 == 1) { + // F_GETFD: get file descriptor flags + if ( + _a0 == FD_STDIN || _a0 == FD_STDOUT || _a0 == FD_STDERR || _a0 == FD_PREIMAGE_READ + || _a0 == FD_HINT_READ || _a0 == FD_PREIMAGE_WRITE || _a0 == FD_HINT_WRITE + ) { + v0_ = 0; // No flags set + } else { + v0_ = U64_MASK; + v1_ = EBADF; + } + } else if (_a1 == 3) { + // F_GETFL: get file status flags + if (_a0 == FD_STDIN || _a0 == FD_PREIMAGE_READ || _a0 == FD_HINT_READ) { + v0_ = 0; // O_RDONLY + } else if (_a0 == FD_STDOUT || _a0 == FD_STDERR || _a0 == FD_PREIMAGE_WRITE || _a0 == FD_HINT_WRITE) { + v0_ = 1; // O_WRONLY + } else { + v0_ = U64_MASK; + v1_ = EBADF; + } + } else { + v0_ = U64_MASK; + v1_ = EINVAL; // cmd not recognized by this kernel + } + + return (v0_, v1_); + } + } + + function handleSyscallUpdates( + st.CpuScalars memory _cpu, + uint64[32] memory _registers, + uint64 _v0, + uint64 _v1 + ) + internal + pure + { + unchecked { + // Write the results back to the state registers + _registers[REG_SYSCALL_RET1] = _v0; + _registers[REG_SYSCALL_ERRNO] = _v1; + + // Update the PC and nextPC + _cpu.pc = _cpu.nextPC; + _cpu.nextPC = _cpu.nextPC + 4; + } + } +} From a68e3ef430383f90001eb3761db492564e3aa2ca Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Wed, 30 Oct 2024 13:58:01 -0600 Subject: [PATCH 080/451] op-deployer: Support overriding proof parameters (#12732) * op-deployer: Support overriding proof parameters This PR adds support for overiding proofs params using the `GlobalDeployOverrides` and `DeployOverrides` fields. Previously, these values were hardcoded. To prevent a recursive import, I refactored the `standard.go` file into a standalone package. Closes https://github.com/ethereum-optimism/optimism/issues/12711. * import cycle * add tests, ability to disable post-checks * rename to dangerously * op-deployer: Add support for deploying interop chains * code review updates --- op-chain-ops/interopgen/deploy.go | 4 +- op-chain-ops/interopgen/recipe.go | 4 +- op-deployer/pkg/deployer/bootstrap/flags.go | 51 ++++- .../bootstrap/{bootstrap.go => opcm.go} | 53 +++++- op-deployer/pkg/deployer/init.go | 1 + .../deployer/integration_test/apply_test.go | 178 +++++++++++++++++- .../pkg/deployer/opcm/artifacts_locator.go | 12 +- op-deployer/pkg/deployer/opcm/opchain.go | 13 +- op-deployer/pkg/deployer/opcm/standard.go | 144 -------------- .../pkg/deployer/pipeline/downloader.go | 4 +- .../pkg/deployer/pipeline/implementations.go | 43 ++++- op-deployer/pkg/deployer/pipeline/init.go | 9 +- op-deployer/pkg/deployer/pipeline/opchain.go | 80 +++++--- .../standard-versions-mainnet.toml | 0 .../standard-versions-sepolia.toml | 0 op-deployer/pkg/deployer/standard/standard.go | 159 ++++++++++++++++ .../pkg/deployer/state/deploy_config.go | 41 +--- op-deployer/pkg/deployer/state/intent.go | 8 +- op-service/jsonutil/merge.go | 37 ++++ .../jsonutil/merge_test.go | 4 +- .../scripts/deploy/DeployOPChain.s.sol | 15 ++ .../test/opcm/DeployOPChain.t.sol | 38 ++++ 22 files changed, 654 insertions(+), 244 deletions(-) rename op-deployer/pkg/deployer/bootstrap/{bootstrap.go => opcm.go} (74%) delete mode 100644 op-deployer/pkg/deployer/opcm/standard.go rename op-deployer/pkg/deployer/{opcm => standard}/standard-versions-mainnet.toml (100%) rename op-deployer/pkg/deployer/{opcm => standard}/standard-versions-sepolia.toml (100%) create mode 100644 op-deployer/pkg/deployer/standard/standard.go create mode 100644 op-service/jsonutil/merge.go rename op-deployer/pkg/deployer/state/deploy_config_test.go => op-service/jsonutil/merge_test.go (91%) diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 83c241fdc013..fb1e71537d55 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum/go-ethereum/common" @@ -173,7 +175,7 @@ func DeploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy, OpcmProxyOwner: superDeployment.SuperchainProxyAdmin, UseInterop: superCfg.Implementations.UseInterop, - StandardVersionsToml: opcm.StandardVersionsMainnetData, + StandardVersionsToml: standard.VersionsMainnetData, }) if err != nil { return nil, fmt.Errorf("failed to deploy Implementations contracts: %w", err) diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index fb76709be663..85f429eb793a 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -79,7 +79,7 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) MipsVersion: big.NewInt(1), }, UseInterop: true, - StandardVersionsToml: opcm.StandardVersionsMainnetData, + StandardVersionsToml: standard.VersionsMainnetData, }, SuperchainL1DeployConfig: genesis.SuperchainL1DeployConfig{ RequiredProtocolVersion: params.OPStackSupport, diff --git a/op-deployer/pkg/deployer/bootstrap/flags.go b/op-deployer/pkg/deployer/bootstrap/flags.go index b76b798b5b0f..d0c9eb5e2ce2 100644 --- a/op-deployer/pkg/deployer/bootstrap/flags.go +++ b/op-deployer/pkg/deployer/bootstrap/flags.go @@ -2,12 +2,19 @@ package bootstrap import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/cliapp" "github.com/urfave/cli/v2" ) const ( - ArtifactsLocatorFlagName = "artifacts-locator" + ArtifactsLocatorFlagName = "artifacts-locator" + WithdrawalDelaySecondsFlagName = "withdrawal-delay-seconds" + MinProposalSizeBytesFlagName = "min-proposal-size-bytes" + ChallengePeriodSecondsFlagName = "challenge-period-seconds" + ProofMaturityDelaySecondsFlagName = "proof-maturity-delay-seconds" + DisputeGameFinalityDelaySecondsFlagName = "dispute-game-finality-delay-seconds" + MIPSVersionFlagName = "mips-version" ) var ( @@ -16,12 +23,54 @@ var ( Usage: "Locator for artifacts.", EnvVars: deployer.PrefixEnvVar("ARTIFACTS_LOCATOR"), } + WithdrawalDelaySecondsFlag = &cli.Uint64Flag{ + Name: WithdrawalDelaySecondsFlagName, + Usage: "Withdrawal delay in seconds.", + EnvVars: deployer.PrefixEnvVar("WITHDRAWAL_DELAY_SECONDS"), + Value: standard.WithdrawalDelaySeconds, + } + MinProposalSizeBytesFlag = &cli.Uint64Flag{ + Name: MinProposalSizeBytesFlagName, + Usage: "Minimum proposal size in bytes.", + EnvVars: deployer.PrefixEnvVar("MIN_PROPOSAL_SIZE_BYTES"), + Value: standard.MinProposalSizeBytes, + } + ChallengePeriodSecondsFlag = &cli.Uint64Flag{ + Name: ChallengePeriodSecondsFlagName, + Usage: "Challenge period in seconds.", + EnvVars: deployer.PrefixEnvVar("CHALLENGE_PERIOD_SECONDS"), + Value: standard.ChallengePeriodSeconds, + } + ProofMaturityDelaySecondsFlag = &cli.Uint64Flag{ + Name: ProofMaturityDelaySecondsFlagName, + Usage: "Proof maturity delay in seconds.", + EnvVars: deployer.PrefixEnvVar("PROOF_MATURITY_DELAY_SECONDS"), + Value: standard.ProofMaturityDelaySeconds, + } + DisputeGameFinalityDelaySecondsFlag = &cli.Uint64Flag{ + Name: DisputeGameFinalityDelaySecondsFlagName, + Usage: "Dispute game finality delay in seconds.", + EnvVars: deployer.PrefixEnvVar("DISPUTE_GAME_FINALITY_DELAY_SECONDS"), + Value: standard.DisputeGameFinalityDelaySeconds, + } + MIPSVersionFlag = &cli.Uint64Flag{ + Name: MIPSVersionFlagName, + Usage: "MIPS version.", + EnvVars: deployer.PrefixEnvVar("MIPS_VERSION"), + Value: standard.MIPSVersion, + } ) var OPCMFlags = []cli.Flag{ deployer.L1RPCURLFlag, deployer.PrivateKeyFlag, ArtifactsLocatorFlag, + WithdrawalDelaySecondsFlag, + MinProposalSizeBytesFlag, + ChallengePeriodSecondsFlag, + ProofMaturityDelaySecondsFlag, + DisputeGameFinalityDelaySecondsFlag, + MIPSVersionFlag, } var Commands = []*cli.Command{ diff --git a/op-deployer/pkg/deployer/bootstrap/bootstrap.go b/op-deployer/pkg/deployer/bootstrap/opcm.go similarity index 74% rename from op-deployer/pkg/deployer/bootstrap/bootstrap.go rename to op-deployer/pkg/deployer/bootstrap/opcm.go index 8bc61698e2a7..4b792ef92160 100644 --- a/op-deployer/pkg/deployer/bootstrap/bootstrap.go +++ b/op-deployer/pkg/deployer/bootstrap/opcm.go @@ -8,6 +8,8 @@ import ( "math/big" "strings" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" @@ -28,6 +30,8 @@ import ( ) type OPCMConfig struct { + pipeline.SuperchainProofParams + L1RPCUrl string PrivateKey string Logger log.Logger @@ -59,6 +63,30 @@ func (c *OPCMConfig) Check() error { return fmt.Errorf("artifacts locator must be specified") } + if c.WithdrawalDelaySeconds == 0 { + c.WithdrawalDelaySeconds = standard.WithdrawalDelaySeconds + } + + if c.MinProposalSizeBytes == 0 { + c.MinProposalSizeBytes = standard.MinProposalSizeBytes + } + + if c.ChallengePeriodSeconds == 0 { + c.ChallengePeriodSeconds = standard.ChallengePeriodSeconds + } + + if c.ProofMaturityDelaySeconds == 0 { + c.ProofMaturityDelaySeconds = standard.ProofMaturityDelaySeconds + } + + if c.DisputeGameFinalityDelaySeconds == 0 { + c.DisputeGameFinalityDelaySeconds = standard.DisputeGameFinalityDelaySeconds + } + + if c.MIPSVersion == 0 { + c.MIPSVersion = standard.MIPSVersion + } + return nil } @@ -82,6 +110,14 @@ func OPCMCLI(cliCtx *cli.Context) error { PrivateKey: privateKey, Logger: l, ArtifactsLocator: artifactsLocator, + SuperchainProofParams: pipeline.SuperchainProofParams{ + WithdrawalDelaySeconds: cliCtx.Uint64(WithdrawalDelaySecondsFlagName), + MinProposalSizeBytes: cliCtx.Uint64(MinProposalSizeBytesFlagName), + ChallengePeriodSeconds: cliCtx.Uint64(ChallengePeriodSecondsFlagName), + ProofMaturityDelaySeconds: cliCtx.Uint64(ProofMaturityDelaySecondsFlagName), + DisputeGameFinalityDelaySeconds: cliCtx.Uint64(DisputeGameFinalityDelaySecondsFlagName), + MIPSVersion: cliCtx.Uint64(MIPSVersionFlagName), + }, }) } @@ -116,15 +152,15 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { } chainIDU64 := chainID.Uint64() - superCfg, err := opcm.SuperchainFor(chainIDU64) + superCfg, err := standard.SuperchainFor(chainIDU64) if err != nil { return fmt.Errorf("error getting superchain config: %w", err) } - standardVersionsTOML, err := opcm.StandardL1VersionsDataFor(chainIDU64) + standardVersionsTOML, err := standard.L1VersionsDataFor(chainIDU64) if err != nil { return fmt.Errorf("error getting standard versions TOML: %w", err) } - opcmProxyOwnerAddr, err := opcm.ManagerOwnerAddrFor(chainIDU64) + opcmProxyOwnerAddr, err := standard.ManagerOwnerAddrFor(chainIDU64) if err != nil { return fmt.Errorf("error getting superchain proxy admin: %w", err) } @@ -192,11 +228,12 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { host, opcm.DeployImplementationsInput{ Salt: salt, - WithdrawalDelaySeconds: big.NewInt(604800), - MinProposalSizeBytes: big.NewInt(126000), - ChallengePeriodSeconds: big.NewInt(86400), - ProofMaturityDelaySeconds: big.NewInt(604800), - DisputeGameFinalityDelaySeconds: big.NewInt(302400), + WithdrawalDelaySeconds: new(big.Int).SetUint64(cfg.WithdrawalDelaySeconds), + MinProposalSizeBytes: new(big.Int).SetUint64(cfg.MinProposalSizeBytes), + ChallengePeriodSeconds: new(big.Int).SetUint64(cfg.ChallengePeriodSeconds), + ProofMaturityDelaySeconds: new(big.Int).SetUint64(cfg.ProofMaturityDelaySeconds), + DisputeGameFinalityDelaySeconds: new(big.Int).SetUint64(cfg.DisputeGameFinalityDelaySeconds), + MipsVersion: new(big.Int).SetUint64(cfg.MIPSVersion), Release: release, SuperchainConfigProxy: superchainConfigAddr, ProtocolVersionsProxy: protocolVersionsAddr, diff --git a/op-deployer/pkg/deployer/init.go b/op-deployer/pkg/deployer/init.go index e8044f75a8c0..6dd26222fb82 100644 --- a/op-deployer/pkg/deployer/init.go +++ b/op-deployer/pkg/deployer/init.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" op_service "github.com/ethereum-optimism/optimism/op-service" diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index e820603dfaf4..cf307c193908 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -14,6 +14,8 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-service/testutils/anvil" @@ -368,10 +370,10 @@ func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, int alloc := chainState.Allocs.Data.Accounts chainIntent := intent.Chains[i] - checkImmutable(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) - checkImmutable(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) - checkImmutable(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) - checkImmutable(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) + checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) // ownership slots var addrAsSlot common.Hash @@ -399,8 +401,12 @@ type bytesMarshaler interface { Bytes() []byte } -func checkImmutable(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { +func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) + checkImmutable(t, allocations, implementationAddress, thing) +} + +func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { account, ok := allocations[implementationAddress] require.True(t, ok, "%s not found in allocations", implementationAddress) require.NotEmpty(t, account.Code, "%s should have code", implementationAddress) @@ -613,6 +619,168 @@ func TestApplyGenesisStrategy(t *testing.T) { } } +func TestProofParamOverrides(t *testing.T) { + op_e2e.InitParallel(t) + + lgr := testlog.Logger(t, slog.LevelDebug) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + depKey := new(deployerKey) + l1ChainID := big.NewInt(77799777) + dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + require.NoError(t, err) + + l2ChainID1 := uint256.NewInt(1) + + deployerAddr, err := dk.Address(depKey) + require.NoError(t, err) + + loc := localArtifactsLocator(t) + + env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) + intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) + intent.DeploymentStrategy = state.DeploymentStrategyGenesis + intent.GlobalDeployOverrides = map[string]any{ + "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, + "minProposalSizeBytes": standard.MinProposalSizeBytes + 1, + "challengePeriodSeconds": standard.ChallengePeriodSeconds + 1, + "proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1, + "disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1, + "mipsVersion": standard.MIPSVersion + 1, + "disputeGameType": standard.DisputeGameType, // This must be set to the permissioned game + "disputeAbsolutePrestate": common.Hash{'A', 'B', 'S', 'O', 'L', 'U', 'T', 'E'}, + "disputeMaxGameDepth": standard.DisputeMaxGameDepth + 1, + "disputeSplitDepth": standard.DisputeSplitDepth + 1, + "disputeClockExtension": standard.DisputeClockExtension + 1, + "disputeMaxClockDuration": standard.DisputeMaxClockDuration + 1, + "dangerouslyAllowCustomDisputeParameters": true, + } + + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) + + allocs := st.L1StateDump.Data.Accounts + chainState := st.Chains[0] + + uint64Caster := func(t *testing.T, val any) common.Hash { + return common.BigToHash(new(big.Int).SetUint64(val.(uint64))) + } + + tests := []struct { + name string + caster func(t *testing.T, val any) common.Hash + address common.Address + }{ + { + "withdrawalDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.DelayedWETHImplAddress, + }, + { + "minProposalSizeBytes", + uint64Caster, + st.ImplementationsDeployment.PreimageOracleSingletonAddress, + }, + { + "challengePeriodSeconds", + uint64Caster, + st.ImplementationsDeployment.PreimageOracleSingletonAddress, + }, + { + "proofMaturityDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.OptimismPortalImplAddress, + }, + { + "disputeGameFinalityDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.OptimismPortalImplAddress, + }, + { + "disputeAbsolutePrestate", + func(t *testing.T, val any) common.Hash { + return val.(common.Hash) + }, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeMaxGameDepth", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeSplitDepth", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeClockExtension", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeMaxClockDuration", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) + }) + } +} + +func TestInteropDeployment(t *testing.T) { + op_e2e.InitParallel(t) + + lgr := testlog.Logger(t, slog.LevelDebug) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + depKey := new(deployerKey) + l1ChainID := big.NewInt(77799777) + dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + require.NoError(t, err) + + l2ChainID1 := uint256.NewInt(1) + + deployerAddr, err := dk.Address(depKey) + require.NoError(t, err) + + loc := localArtifactsLocator(t) + + env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) + intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) + intent.DeploymentStrategy = state.DeploymentStrategyGenesis + intent.UseInterop = true + + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) + + chainState := st.Chains[0] + depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") + checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) + proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes()) + checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) + +} + func TestInvalidL2Genesis(t *testing.T) { op_e2e.InitParallel(t) diff --git a/op-deployer/pkg/deployer/opcm/artifacts_locator.go b/op-deployer/pkg/deployer/opcm/artifacts_locator.go index 1db025393e62..02a2b60b59fb 100644 --- a/op-deployer/pkg/deployer/opcm/artifacts_locator.go +++ b/op-deployer/pkg/deployer/opcm/artifacts_locator.go @@ -4,6 +4,8 @@ import ( "fmt" "net/url" "strings" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" ) type schemeUnmarshaler func(string) (*ArtifactsLocator, error) @@ -14,6 +16,14 @@ var schemeUnmarshalerDispatch = map[string]schemeUnmarshaler{ "https": unmarshalURL, } +var DefaultL1ContractsLocator = &ArtifactsLocator{ + Tag: standard.DefaultL1ContractsTag, +} + +var DefaultL2ContractsLocator = &ArtifactsLocator{ + Tag: standard.DefaultL2ContractsTag, +} + type ArtifactsLocator struct { URL *url.URL Tag string @@ -61,7 +71,7 @@ func unmarshalTag(tag string) (*ArtifactsLocator, error) { return nil, fmt.Errorf("invalid tag: %s", tag) } - if _, err := StandardArtifactsURLForTag(tag); err != nil { + if _, err := standard.ArtifactsURLForTag(tag); err != nil { return nil, err } diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index beefad5f2630..b6bf4fcb6b7c 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -38,12 +38,13 @@ type DeployOPChainInput struct { SaltMixer string GasLimit uint64 - DisputeGameType uint32 - DisputeAbsolutePrestate common.Hash - DisputeMaxGameDepth uint64 - DisputeSplitDepth uint64 - DisputeClockExtension uint64 - DisputeMaxClockDuration uint64 + DisputeGameType uint32 + DisputeAbsolutePrestate common.Hash + DisputeMaxGameDepth uint64 + DisputeSplitDepth uint64 + DisputeClockExtension uint64 + DisputeMaxClockDuration uint64 + AllowCustomDisputeParameters bool } func (input *DeployOPChainInput) InputSet() bool { diff --git a/op-deployer/pkg/deployer/opcm/standard.go b/op-deployer/pkg/deployer/opcm/standard.go deleted file mode 100644 index 5d85db942770..000000000000 --- a/op-deployer/pkg/deployer/opcm/standard.go +++ /dev/null @@ -1,144 +0,0 @@ -package opcm - -import ( - "embed" - "fmt" - "net/url" - - "github.com/BurntSushi/toml" - - "github.com/ethereum-optimism/superchain-registry/superchain" - "github.com/ethereum/go-ethereum/common" -) - -//go:embed standard-versions-mainnet.toml -var StandardVersionsMainnetData string - -//go:embed standard-versions-sepolia.toml -var StandardVersionsSepoliaData string - -var StandardL1VersionsSepolia StandardL1Versions - -var StandardL1VersionsMainnet StandardL1Versions - -var DefaultL1ContractsLocator = &ArtifactsLocator{ - Tag: "op-contracts/v1.6.0", -} - -var DefaultL2ContractsLocator = &ArtifactsLocator{ - Tag: "op-contracts/v1.7.0-beta.1+l2-contracts", -} - -type StandardL1Versions struct { - Releases map[string]StandardL1VersionsReleases `toml:"releases"` -} - -type StandardL1VersionsReleases struct { - OptimismPortal StandardVersionRelease `toml:"optimism_portal"` - SystemConfig StandardVersionRelease `toml:"system_config"` - AnchorStateRegistry StandardVersionRelease `toml:"anchor_state_registry"` - DelayedWETH StandardVersionRelease `toml:"delayed_weth"` - DisputeGameFactory StandardVersionRelease `toml:"dispute_game_factory"` - FaultDisputeGame StandardVersionRelease `toml:"fault_dispute_game"` - PermissionedDisputeGame StandardVersionRelease `toml:"permissioned_dispute_game"` - MIPS StandardVersionRelease `toml:"mips"` - PreimageOracle StandardVersionRelease `toml:"preimage_oracle"` - L1CrossDomainMessenger StandardVersionRelease `toml:"l1_cross_domain_messenger"` - L1ERC721Bridge StandardVersionRelease `toml:"l1_erc721_bridge"` - L1StandardBridge StandardVersionRelease `toml:"l1_standard_bridge"` - OptimismMintableERC20Factory StandardVersionRelease `toml:"optimism_mintable_erc20_factory"` -} - -type StandardVersionRelease struct { - Version string `toml:"version"` - ImplementationAddress common.Address `toml:"implementation_address"` - Address common.Address `toml:"address"` -} - -var _ embed.FS - -func StandardL1VersionsDataFor(chainID uint64) (string, error) { - switch chainID { - case 1: - return StandardVersionsMainnetData, nil - case 11155111: - return StandardVersionsSepoliaData, nil - default: - return "", fmt.Errorf("unsupported chain ID: %d", chainID) - } -} - -func StandardL1VersionsFor(chainID uint64) (StandardL1Versions, error) { - switch chainID { - case 1: - return StandardL1VersionsMainnet, nil - case 11155111: - return StandardL1VersionsSepolia, nil - default: - return StandardL1Versions{}, fmt.Errorf("unsupported chain ID: %d", chainID) - } -} - -func SuperchainFor(chainID uint64) (*superchain.Superchain, error) { - switch chainID { - case 1: - return superchain.Superchains["mainnet"], nil - case 11155111: - return superchain.Superchains["sepolia"], nil - default: - return nil, fmt.Errorf("unsupported chain ID: %d", chainID) - } -} - -func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) { - switch chainID { - case 1: - // Generated using the bootstrap command on 10/18/2024. - return common.HexToAddress("0x18cec91779995ad14c880e4095456b9147160790"), nil - case 11155111: - // Generated using the bootstrap command on 10/18/2024. - return common.HexToAddress("0xf564eea7960ea244bfebcbbb17858748606147bf"), nil - default: - return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) - } -} - -func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) { - switch chainID { - case 1: - // Set to superchain proxy admin - return common.HexToAddress("0x543bA4AADBAb8f9025686Bd03993043599c6fB04"), nil - case 11155111: - // Set to development multisig - return common.HexToAddress("0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B"), nil - default: - return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) - } -} - -func StandardArtifactsURLForTag(tag string) (*url.URL, error) { - switch tag { - case "op-contracts/v1.6.0": - return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f")) - case "op-contracts/v1.7.0-beta.1+l2-contracts": - return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597")) - default: - return nil, fmt.Errorf("unsupported tag: %s", tag) - } -} - -func standardArtifactsURL(checksum string) string { - return fmt.Sprintf("https://storage.googleapis.com/oplabs-contract-artifacts/artifacts-v1-%s.tar.gz", checksum) -} - -func init() { - StandardL1VersionsMainnet = StandardL1Versions{} - if err := toml.Unmarshal([]byte(StandardVersionsMainnetData), &StandardL1VersionsMainnet); err != nil { - panic(err) - } - - StandardL1VersionsSepolia = StandardL1Versions{} - if err := toml.Unmarshal([]byte(StandardVersionsSepoliaData), &StandardL1VersionsSepolia); err != nil { - panic(err) - } -} diff --git a/op-deployer/pkg/deployer/pipeline/downloader.go b/op-deployer/pkg/deployer/pipeline/downloader.go index 370817e8366a..ffafd6096869 100644 --- a/op-deployer/pkg/deployer/pipeline/downloader.go +++ b/op-deployer/pkg/deployer/pipeline/downloader.go @@ -15,6 +15,8 @@ import ( "strings" "time" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" @@ -42,7 +44,7 @@ func DownloadArtifacts(ctx context.Context, loc *opcm.ArtifactsLocator, progress var u *url.URL var err error if loc.IsTag() { - u, err = opcm.StandardArtifactsURLForTag(loc.Tag) + u, err = standard.ArtifactsURLForTag(loc.Tag) if err != nil { return nil, nil, fmt.Errorf("failed to get standard artifacts URL for tag %s: %w", loc.Tag, err) } diff --git a/op-deployer/pkg/deployer/pipeline/implementations.go b/op-deployer/pkg/deployer/pipeline/implementations.go index 17628ec605cd..9da1d4a900a5 100644 --- a/op-deployer/pkg/deployer/pipeline/implementations.go +++ b/op-deployer/pkg/deployer/pipeline/implementations.go @@ -4,10 +4,22 @@ import ( "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" ) +type SuperchainProofParams struct { + WithdrawalDelaySeconds uint64 `json:"withdrawalDelaySeconds" toml:"withdrawalDelaySeconds"` + MinProposalSizeBytes uint64 `json:"minProposalSizeBytes" toml:"minProposalSizeBytes"` + ChallengePeriodSeconds uint64 `json:"challengePeriodSeconds" toml:"challengePeriodSeconds"` + ProofMaturityDelaySeconds uint64 `json:"proofMaturityDelaySeconds" toml:"proofMaturityDelaySeconds"` + DisputeGameFinalityDelaySeconds uint64 `json:"disputeGameFinalityDelaySeconds" toml:"disputeGameFinalityDelaySeconds"` + MIPSVersion uint64 `json:"mipsVersion" toml:"mipsVersion"` +} + func DeployImplementations(env *Env, intent *state.Intent, st *state.State) error { lgr := env.Logger.New("stage", "deploy-implementations") @@ -22,7 +34,7 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro var contractsRelease string var err error if intent.L1ContractsLocator.IsTag() && intent.DeploymentStrategy == state.DeploymentStrategyLive { - standardVersionsTOML, err = opcm.StandardL1VersionsDataFor(intent.L1ChainID) + standardVersionsTOML, err = standard.L1VersionsDataFor(intent.L1ChainID) if err != nil { return fmt.Errorf("error getting standard versions TOML: %w", err) } @@ -31,24 +43,39 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro contractsRelease = "dev" } + proofParams, err := jsonutil.MergeJSON( + SuperchainProofParams{ + WithdrawalDelaySeconds: standard.WithdrawalDelaySeconds, + MinProposalSizeBytes: standard.MinProposalSizeBytes, + ChallengePeriodSeconds: standard.ChallengePeriodSeconds, + ProofMaturityDelaySeconds: standard.ProofMaturityDelaySeconds, + DisputeGameFinalityDelaySeconds: standard.DisputeGameFinalityDelaySeconds, + MIPSVersion: standard.MIPSVersion, + }, + intent.GlobalDeployOverrides, + ) + if err != nil { + return fmt.Errorf("error merging proof params from overrides: %w", err) + } + env.L1ScriptHost.ImportState(st.L1StateDump.Data) dio, err := opcm.DeployImplementations( env.L1ScriptHost, opcm.DeployImplementationsInput{ Salt: st.Create2Salt, - WithdrawalDelaySeconds: big.NewInt(604800), - MinProposalSizeBytes: big.NewInt(126000), - ChallengePeriodSeconds: big.NewInt(86400), - ProofMaturityDelaySeconds: big.NewInt(604800), - DisputeGameFinalityDelaySeconds: big.NewInt(302400), - MipsVersion: big.NewInt(1), + WithdrawalDelaySeconds: new(big.Int).SetUint64(proofParams.WithdrawalDelaySeconds), + MinProposalSizeBytes: new(big.Int).SetUint64(proofParams.MinProposalSizeBytes), + ChallengePeriodSeconds: new(big.Int).SetUint64(proofParams.ChallengePeriodSeconds), + ProofMaturityDelaySeconds: new(big.Int).SetUint64(proofParams.ProofMaturityDelaySeconds), + DisputeGameFinalityDelaySeconds: new(big.Int).SetUint64(proofParams.DisputeGameFinalityDelaySeconds), + MipsVersion: new(big.Int).SetUint64(proofParams.MIPSVersion), Release: contractsRelease, SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress, ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress, OpcmProxyOwner: st.SuperchainDeployment.ProxyAdminAddress, StandardVersionsToml: standardVersionsTOML, - UseInterop: false, + UseInterop: intent.UseInterop, }, ) if err != nil { diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index 9d5f2324fa45..f8b12e36a431 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -5,7 +5,8 @@ import ( "crypto/rand" "fmt" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/script" @@ -26,12 +27,12 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s } if intent.L1ContractsLocator.IsTag() { - superCfg, err := opcm.SuperchainFor(intent.L1ChainID) + superCfg, err := standard.SuperchainFor(intent.L1ChainID) if err != nil { return fmt.Errorf("error getting superchain config: %w", err) } - proxyAdmin, err := opcm.ManagerOwnerAddrFor(intent.L1ChainID) + proxyAdmin, err := standard.ManagerOwnerAddrFor(intent.L1ChainID) if err != nil { return fmt.Errorf("error getting superchain proxy admin address: %w", err) } @@ -44,7 +45,7 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr), } - opcmProxy, err := opcm.ManagerImplementationAddrFor(intent.L1ChainID) + opcmProxy, err := standard.ManagerImplementationAddrFor(intent.L1ChainID) if err != nil { return fmt.Errorf("error getting OPCM proxy address: %w", err) } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 5152eca3208a..ebc6c1af0bc8 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -6,6 +6,9 @@ import ( "errors" "fmt" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" @@ -26,7 +29,10 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu return fmt.Errorf("failed to get chain intent: %w", err) } - input := makeDCI(thisIntent, chainID, st) + input, err := makeDCI(intent, thisIntent, chainID, st) + if err != nil { + return fmt.Errorf("error making deploy OP chain input: %w", err) + } var dco opcm.DeployOPChainOutput lgr.Info("deploying OP chain using existing OPCM", "id", chainID.Hex(), "opcmAddress", st.ImplementationsDeployment.OpcmProxyAddress.Hex()) @@ -152,7 +158,10 @@ func DeployOPChainGenesisStrategy(env *Env, intent *state.Intent, st *state.Stat return fmt.Errorf("failed to get chain intent: %w", err) } - input := makeDCI(thisIntent, chainID, st) + input, err := makeDCI(intent, thisIntent, chainID, st) + if err != nil { + return fmt.Errorf("error making deploy OP chain input: %w", err) + } env.L1ScriptHost.ImportState(st.L1StateDump.Data) @@ -171,27 +180,54 @@ func DeployOPChainGenesisStrategy(env *Env, intent *state.Intent, st *state.Stat return nil } -func makeDCI(thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) opcm.DeployOPChainInput { - return opcm.DeployOPChainInput{ - OpChainProxyAdminOwner: thisIntent.Roles.L1ProxyAdminOwner, - SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, - Batcher: thisIntent.Roles.Batcher, - UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner, - Proposer: thisIntent.Roles.Proposer, - Challenger: thisIntent.Roles.Challenger, - BasefeeScalar: 1368, - BlobBaseFeeScalar: 801949, - L2ChainId: chainID.Big(), - OpcmProxy: st.ImplementationsDeployment.OpcmProxyAddress, - SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization - GasLimit: 60_000_000, - DisputeGameType: 1, // PERMISSIONED_CANNON Game Type - DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"), - DisputeMaxGameDepth: 73, - DisputeSplitDepth: 30, - DisputeClockExtension: 10800, // 3 hours (input in seconds) - DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds) +type ChainProofParams struct { + DisputeGameType uint32 `json:"disputeGameType" toml:"disputeGameType"` + DisputeAbsolutePrestate common.Hash `json:"disputeAbsolutePrestate" toml:"disputeAbsolutePrestate"` + DisputeMaxGameDepth uint64 `json:"disputeMaxGameDepth" toml:"disputeMaxGameDepth"` + DisputeSplitDepth uint64 `json:"disputeSplitDepth" toml:"disputeSplitDepth"` + DisputeClockExtension uint64 `json:"disputeClockExtension" toml:"disputeClockExtension"` + DisputeMaxClockDuration uint64 `json:"disputeMaxClockDuration" toml:"disputeMaxClockDuration"` + DangerouslyAllowCustomDisputeParameters bool `json:"dangerouslyAllowCustomDisputeParameters" toml:"dangerouslyAllowCustomDisputeParameters"` +} + +func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInput, error) { + proofParams, err := jsonutil.MergeJSON( + ChainProofParams{ + DisputeGameType: standard.DisputeGameType, + DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, + DisputeMaxGameDepth: standard.DisputeMaxGameDepth, + DisputeSplitDepth: standard.DisputeSplitDepth, + DisputeClockExtension: standard.DisputeClockExtension, + DisputeMaxClockDuration: standard.DisputeMaxClockDuration, + }, + intent.GlobalDeployOverrides, + thisIntent.DeployOverrides, + ) + if err != nil { + return opcm.DeployOPChainInput{}, fmt.Errorf("error merging proof params from overrides: %w", err) } + + return opcm.DeployOPChainInput{ + OpChainProxyAdminOwner: thisIntent.Roles.L1ProxyAdminOwner, + SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, + Batcher: thisIntent.Roles.Batcher, + UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner, + Proposer: thisIntent.Roles.Proposer, + Challenger: thisIntent.Roles.Challenger, + BasefeeScalar: standard.BasefeeScalar, + BlobBaseFeeScalar: standard.BlobBaseFeeScalar, + L2ChainId: chainID.Big(), + OpcmProxy: st.ImplementationsDeployment.OpcmProxyAddress, + SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization + GasLimit: standard.GasLimit, + DisputeGameType: proofParams.DisputeGameType, + DisputeAbsolutePrestate: proofParams.DisputeAbsolutePrestate, + DisputeMaxGameDepth: proofParams.DisputeMaxGameDepth, + DisputeSplitDepth: proofParams.DisputeSplitDepth, + DisputeClockExtension: proofParams.DisputeClockExtension, // 3 hours (input in seconds) + DisputeMaxClockDuration: proofParams.DisputeMaxClockDuration, // 3.5 days (input in seconds) + AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters, + }, nil } func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.ChainState { diff --git a/op-deployer/pkg/deployer/opcm/standard-versions-mainnet.toml b/op-deployer/pkg/deployer/standard/standard-versions-mainnet.toml similarity index 100% rename from op-deployer/pkg/deployer/opcm/standard-versions-mainnet.toml rename to op-deployer/pkg/deployer/standard/standard-versions-mainnet.toml diff --git a/op-deployer/pkg/deployer/opcm/standard-versions-sepolia.toml b/op-deployer/pkg/deployer/standard/standard-versions-sepolia.toml similarity index 100% rename from op-deployer/pkg/deployer/opcm/standard-versions-sepolia.toml rename to op-deployer/pkg/deployer/standard/standard-versions-sepolia.toml diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go new file mode 100644 index 000000000000..ba3031bbe99e --- /dev/null +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -0,0 +1,159 @@ +package standard + +import ( + "embed" + "fmt" + "net/url" + + "github.com/BurntSushi/toml" + + "github.com/ethereum-optimism/superchain-registry/superchain" + "github.com/ethereum/go-ethereum/common" +) + +const ( + GasLimit uint64 = 60_000_000 + BasefeeScalar uint32 = 1368 + BlobBaseFeeScalar uint32 = 801949 + WithdrawalDelaySeconds uint64 = 604800 + MinProposalSizeBytes uint64 = 126000 + ChallengePeriodSeconds uint64 = 86400 + ProofMaturityDelaySeconds uint64 = 604800 + DisputeGameFinalityDelaySeconds uint64 = 302400 + MIPSVersion uint64 = 1 + DisputeGameType uint32 = 1 // PERMISSIONED game type + DisputeMaxGameDepth uint64 = 73 + DisputeSplitDepth uint64 = 30 + DisputeClockExtension uint64 = 10800 + DisputeMaxClockDuration uint64 = 302400 +) + +var DisputeAbsolutePrestate = common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") + +//go:embed standard-versions-mainnet.toml +var VersionsMainnetData string + +//go:embed standard-versions-sepolia.toml +var VersionsSepoliaData string + +var L1VersionsSepolia L1Versions + +var L1VersionsMainnet L1Versions + +var DefaultL1ContractsTag = "op-contracts/v1.6.0" + +var DefaultL2ContractsTag = "op-contracts/v1.7.0-beta.1+l2-contracts" + +type L1Versions struct { + Releases map[string]L1VersionsReleases `toml:"releases"` +} + +type L1VersionsReleases struct { + OptimismPortal VersionRelease `toml:"optimism_portal"` + SystemConfig VersionRelease `toml:"system_config"` + AnchorStateRegistry VersionRelease `toml:"anchor_state_registry"` + DelayedWETH VersionRelease `toml:"delayed_weth"` + DisputeGameFactory VersionRelease `toml:"dispute_game_factory"` + FaultDisputeGame VersionRelease `toml:"fault_dispute_game"` + PermissionedDisputeGame VersionRelease `toml:"permissioned_dispute_game"` + MIPS VersionRelease `toml:"mips"` + PreimageOracle VersionRelease `toml:"preimage_oracle"` + L1CrossDomainMessenger VersionRelease `toml:"l1_cross_domain_messenger"` + L1ERC721Bridge VersionRelease `toml:"l1_erc721_bridge"` + L1StandardBridge VersionRelease `toml:"l1_standard_bridge"` + OptimismMintableERC20Factory VersionRelease `toml:"optimism_mintable_erc20_factory"` +} + +type VersionRelease struct { + Version string `toml:"version"` + ImplementationAddress common.Address `toml:"implementation_address"` + Address common.Address `toml:"address"` +} + +var _ embed.FS + +func L1VersionsDataFor(chainID uint64) (string, error) { + switch chainID { + case 1: + return VersionsMainnetData, nil + case 11155111: + return VersionsSepoliaData, nil + default: + return "", fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func L1VersionsFor(chainID uint64) (L1Versions, error) { + switch chainID { + case 1: + return L1VersionsMainnet, nil + case 11155111: + return L1VersionsSepolia, nil + default: + return L1Versions{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func SuperchainFor(chainID uint64) (*superchain.Superchain, error) { + switch chainID { + case 1: + return superchain.Superchains["mainnet"], nil + case 11155111: + return superchain.Superchains["sepolia"], nil + default: + return nil, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + // Generated using the bootstrap command on 10/18/2024. + return common.HexToAddress("0x18cec91779995ad14c880e4095456b9147160790"), nil + case 11155111: + // Generated using the bootstrap command on 10/18/2024. + return common.HexToAddress("0xf564eea7960ea244bfebcbbb17858748606147bf"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + // Set to superchain proxy admin + return common.HexToAddress("0x543bA4AADBAb8f9025686Bd03993043599c6fB04"), nil + case 11155111: + // Set to development multisig + return common.HexToAddress("0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func ArtifactsURLForTag(tag string) (*url.URL, error) { + switch tag { + case "op-contracts/v1.6.0": + return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f")) + case "op-contracts/v1.7.0-beta.1+l2-contracts": + return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597")) + default: + return nil, fmt.Errorf("unsupported tag: %s", tag) + } +} + +func standardArtifactsURL(checksum string) string { + return fmt.Sprintf("https://storage.googleapis.com/oplabs-contract-artifacts/artifacts-v1-%s.tar.gz", checksum) +} + +func init() { + L1VersionsMainnet = L1Versions{} + if err := toml.Unmarshal([]byte(VersionsMainnetData), &L1VersionsMainnet); err != nil { + panic(err) + } + + L1VersionsSepolia = L1Versions{} + if err := toml.Unmarshal([]byte(VersionsSepoliaData), &L1VersionsSepolia); err != nil { + panic(err) + } +} diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 2bc8307ab9d8..534b1292d125 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -1,10 +1,11 @@ package state import ( - "encoding/json" "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/common" @@ -135,7 +136,7 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, // Apply overrides after setting the main values. var err error if len(intent.GlobalDeployOverrides) > 0 { - cfg, err = mergeJSON(cfg, intent.GlobalDeployOverrides) + cfg, err = jsonutil.MergeJSON(cfg, intent.GlobalDeployOverrides) if err != nil { return genesis.DeployConfig{}, fmt.Errorf("error merging global L2 overrides: %w", err) @@ -143,7 +144,7 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, } if len(chainIntent.DeployOverrides) > 0 { - cfg, err = mergeJSON(cfg, chainIntent.DeployOverrides) + cfg, err = jsonutil.MergeJSON(cfg, chainIntent.DeployOverrides) if err != nil { return genesis.DeployConfig{}, fmt.Errorf("error merging chain L2 overrides: %w", err) } @@ -156,40 +157,6 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, return cfg, nil } -// mergeJSON merges the provided overrides into the input struct. Fields -// must be JSON-serializable for this to work. Overrides are applied in -// order of precedence - i.e., the last overrides will override keys from -// all preceding overrides. -func mergeJSON[T any](in T, overrides ...map[string]any) (T, error) { - var out T - inJSON, err := json.Marshal(in) - if err != nil { - return out, err - } - - var tmpMap map[string]interface{} - if err := json.Unmarshal(inJSON, &tmpMap); err != nil { - return out, err - } - - for _, override := range overrides { - for k, v := range override { - tmpMap[k] = v - } - } - - inJSON, err = json.Marshal(tmpMap) - if err != nil { - return out, err - } - - if err := json.Unmarshal(inJSON, &out); err != nil { - return out, err - } - - return out, nil -} - func mustHexBigFromHex(hex string) *hexutil.Big { num := hexutil.MustDecodeBig(hex) hexBig := hexutil.Big(*num) diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index f43e9ff26cae..cbabe37fc8ef 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -4,6 +4,8 @@ import ( "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-service/ioutil" @@ -38,6 +40,8 @@ type Intent struct { FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"` + UseInterop bool `json:"useInterop" toml:"useInterop"` + L1ContractsLocator *opcm.ArtifactsLocator `json:"l1ContractsLocator" toml:"l1ContractsLocator"` L2ContractsLocator *opcm.ArtifactsLocator `json:"l2ContractsLocator" toml:"l2ContractsLocator"` @@ -102,7 +106,7 @@ func (c *Intent) WriteToFile(path string) error { } func (c *Intent) checkL1Prod() error { - versions, err := opcm.StandardL1VersionsFor(c.L1ChainID) + versions, err := standard.L1VersionsFor(c.L1ChainID) if err != nil { return err } @@ -131,7 +135,7 @@ func (c *Intent) checkL1Dev() error { } func (c *Intent) checkL2Prod() error { - _, err := opcm.StandardArtifactsURLForTag(c.L2ContractsLocator.Tag) + _, err := standard.ArtifactsURLForTag(c.L2ContractsLocator.Tag) return err } diff --git a/op-service/jsonutil/merge.go b/op-service/jsonutil/merge.go new file mode 100644 index 000000000000..8819b2ad1993 --- /dev/null +++ b/op-service/jsonutil/merge.go @@ -0,0 +1,37 @@ +package jsonutil + +import "encoding/json" + +// MergeJSON merges the provided overrides into the input struct. Fields +// must be JSON-serializable for this to work. Overrides are applied in +// order of precedence - i.e., the last overrides will override keys from +// all preceding overrides. +func MergeJSON[T any](in T, overrides ...map[string]any) (T, error) { + var out T + inJSON, err := json.Marshal(in) + if err != nil { + return out, err + } + + var tmpMap map[string]interface{} + if err := json.Unmarshal(inJSON, &tmpMap); err != nil { + return out, err + } + + for _, override := range overrides { + for k, v := range override { + tmpMap[k] = v + } + } + + inJSON, err = json.Marshal(tmpMap) + if err != nil { + return out, err + } + + if err := json.Unmarshal(inJSON, &out); err != nil { + return out, err + } + + return out, nil +} diff --git a/op-deployer/pkg/deployer/state/deploy_config_test.go b/op-service/jsonutil/merge_test.go similarity index 91% rename from op-deployer/pkg/deployer/state/deploy_config_test.go rename to op-service/jsonutil/merge_test.go index be431b5740ce..da0a400c651c 100644 --- a/op-deployer/pkg/deployer/state/deploy_config_test.go +++ b/op-service/jsonutil/merge_test.go @@ -1,4 +1,4 @@ -package state +package jsonutil import ( "testing" @@ -13,7 +13,7 @@ func TestMergeJSON(t *testing.T) { C bool `json:"c"` } - out, err := mergeJSON( + out, err := MergeJSON( testStruct{ "hello", 42, diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index bc83402045d0..7a5d1f5cc6b2 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -58,6 +58,7 @@ contract DeployOPChainInput is BaseDeployIO { uint256 internal _disputeSplitDepth; Duration internal _disputeClockExtension; Duration internal _disputeMaxClockDuration; + bool internal _allowCustomDisputeParameters; function set(bytes4 _sel, address _addr) public { require(_addr != address(0), "DeployOPChainInput: cannot set zero address"); @@ -107,6 +108,11 @@ contract DeployOPChainInput is BaseDeployIO { else revert("DeployImplementationsInput: unknown selector"); } + function set(bytes4 _sel, bool _value) public { + if (_sel == this.allowCustomDisputeParameters.selector) _allowCustomDisputeParameters = _value; + else revert("DeployOPChainInput: unknown selector"); + } + function opChainProxyAdminOwner() public view returns (address) { require(_opChainProxyAdminOwner != address(0), "DeployOPChainInput: not set"); return _opChainProxyAdminOwner; @@ -206,6 +212,10 @@ contract DeployOPChainInput is BaseDeployIO { function disputeMaxClockDuration() public view returns (Duration) { return _disputeMaxClockDuration; } + + function allowCustomDisputeParameters() public view returns (bool) { + return _allowCustomDisputeParameters; + } } contract DeployOPChainOutput is BaseDeployIO { @@ -457,6 +467,11 @@ contract DeployOPChain is Script { IPermissionedDisputeGame game = _doo.permissionedDisputeGame(); require(GameType.unwrap(game.gameType()) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON), "DPG-10"); + + if (_doi.allowCustomDisputeParameters()) { + return; + } + // This hex string is the absolutePrestate of the latest op-program release, see where the // `EXPECTED_PRESTATE_HASH` is defined in `config.yml`. require( diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index edfe4574bc7c..54e0e456def3 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -59,6 +59,7 @@ contract DeployOPChainInput_Test is Test { doi.set(doi.basefeeScalar.selector, basefeeScalar); doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); + doi.set(doi.allowCustomDisputeParameters.selector, true); (IProxy opcmProxy) = DeployUtils.buildERC1967ProxyWithImpl("opcmProxy"); doi.set(doi.opcmProxy.selector, address(opcmProxy)); @@ -74,6 +75,7 @@ contract DeployOPChainInput_Test is Test { assertEq(blobBaseFeeScalar, doi.blobBaseFeeScalar(), "900"); assertEq(l2ChainId, doi.l2ChainId(), "1000"); assertEq(address(opcmProxy), address(doi.opcmProxy()), "1100"); + assertEq(true, doi.allowCustomDisputeParameters(), "1200"); } function test_getters_whenNotSet_revert() public { @@ -531,6 +533,42 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { assertEq(address(doo.opChainProxyAdmin().addressManager()), address(doo.addressManager()), "3700"); assertEq(address(doo.opChainProxyAdmin().owner()), opChainProxyAdminOwner, "3800"); } + + function test_customDisputeGame_customDisabled_reverts() public { + setDOI(); + doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth + 1); + vm.expectRevert("DPG-90"); + deployOPChain.run(doi, doo); + } + + function test_customDisputeGame_customEnabled_doesNotRevert() public { + setDOI(); + doi.set(doi.allowCustomDisputeParameters.selector, true); + doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth + 1); + deployOPChain.run(doi, doo); + assertEq(doo.permissionedDisputeGame().splitDepth(), disputeSplitDepth + 1); + } + + function setDOI() internal { + doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); + doi.set(doi.systemConfigOwner.selector, systemConfigOwner); + doi.set(doi.batcher.selector, batcher); + doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); + doi.set(doi.proposer.selector, proposer); + doi.set(doi.challenger.selector, challenger); + doi.set(doi.basefeeScalar.selector, basefeeScalar); + doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); + doi.set(doi.l2ChainId.selector, l2ChainId); + doi.set(doi.opcmProxy.selector, address(opcm)); + doi.set(doi.saltMixer.selector, saltMixer); + doi.set(doi.gasLimit.selector, gasLimit); + doi.set(doi.disputeGameType.selector, disputeGameType); + doi.set(doi.disputeAbsolutePrestate.selector, disputeAbsolutePrestate); + doi.set(doi.disputeMaxGameDepth.selector, disputeMaxGameDepth); + doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth); + doi.set(doi.disputeClockExtension.selector, disputeClockExtension); + doi.set(doi.disputeMaxClockDuration.selector, disputeMaxClockDuration); + } } contract DeployOPChain_Test_Interop is DeployOPChain_Test { From aeaee21725b34b0aeacebf9300cff064d6228e05 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 30 Oct 2024 13:38:56 -0700 Subject: [PATCH 081/451] cannon: Avoid page allocation for empty reads (#12747) * cannon: Avoid page allocation for empty reads * fix end of chunk overlap --- cannon/mipsevm/memory/memory.go | 16 ++++--- cannon/mipsevm/memory/memory64_test.go | 45 +++++++++++++++++++ cannon/mipsevm/memory/memory_test.go | 45 +++++++++++++++++++ .../mipsevm/tests/fuzz_evm_common64_test.go | 6 --- 4 files changed, 100 insertions(+), 12 deletions(-) diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index e95b40ca5346..1234af4dc4b6 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -280,18 +280,22 @@ func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { for { pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask - p, ok := m.pageLookup(pageIndex) - if !ok { - p = m.AllocPage(pageIndex) - } - p.InvalidateFull() - n, err := r.Read(p.Data[pageAddr:]) + readLen := PageSize - pageAddr + chunk := make([]byte, readLen) + n, err := r.Read(chunk) if err != nil { if err == io.EOF { return nil } return err } + + p, ok := m.pageLookup(pageIndex) + if !ok { + p = m.AllocPage(pageIndex) + } + p.InvalidateFull() + copy(p.Data[pageAddr:], chunk[:n]) addr += Word(n) } } diff --git a/cannon/mipsevm/memory/memory64_test.go b/cannon/mipsevm/memory/memory64_test.go index 38cbfd597ffc..203ff5f80d2f 100644 --- a/cannon/mipsevm/memory/memory64_test.go +++ b/cannon/mipsevm/memory/memory64_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/stretchr/testify/require" ) @@ -140,6 +141,50 @@ func TestMemory64ReadWrite(t *testing.T) { require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end") }) + t.Run("empty range", func(t *testing.T) { + m := NewMemory() + addr := Word(0xAABBCC00) + r := bytes.NewReader(nil) + pre := m.MerkleRoot() + preJSON, err := m.MarshalJSON() + require.NoError(t, err) + var preSerialized bytes.Buffer + require.NoError(t, m.Serialize(&preSerialized)) + + require.NoError(t, m.SetMemoryRange(addr, r)) + v := m.GetWord(0) + require.Equal(t, Word(0), v) + post := m.MerkleRoot() + require.Equal(t, pre, post) + + // Assert that there are no extra zero pages in serialization + postJSON, err := m.MarshalJSON() + require.NoError(t, err) + require.Equal(t, preJSON, postJSON) + + var postSerialized bytes.Buffer + require.NoError(t, m.Serialize(&postSerialized)) + require.Equal(t, preSerialized.Bytes(), postSerialized.Bytes()) + }) + + t.Run("range page overlap", func(t *testing.T) { + m := NewMemory() + data := bytes.Repeat([]byte{0xAA}, PageAddrSize) + require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) + for i := 0; i < PageAddrSize/arch.WordSizeBytes; i++ { + addr := Word(i * arch.WordSizeBytes) + require.Equal(t, Word(0xAAAAAAAA_AAAAAAAA), m.GetWord(addr)) + } + + data = []byte{0x11, 0x22, 0x33, 0x44} + require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) + require.Equal(t, Word(0x11223344_AAAAAAAA), m.GetWord(0)) + for i := 1; i < PageAddrSize/arch.WordSizeBytes; i++ { + addr := Word(i * arch.WordSizeBytes) + require.Equal(t, Word(0xAAAAAAAA_AAAAAAAA), m.GetWord(addr)) + } + }) + t.Run("read-write", func(t *testing.T) { m := NewMemory() m.SetWord(16, 0xAABBCCDD_EEFF1122) diff --git a/cannon/mipsevm/memory/memory_test.go b/cannon/mipsevm/memory/memory_test.go index 3a8bdd364d80..5100de5b4159 100644 --- a/cannon/mipsevm/memory/memory_test.go +++ b/cannon/mipsevm/memory/memory_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/stretchr/testify/require" ) @@ -139,6 +140,50 @@ func TestMemoryReadWrite(t *testing.T) { require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end") }) + t.Run("empty range", func(t *testing.T) { + m := NewMemory() + addr := Word(0xAABBCC00) + r := bytes.NewReader(nil) + pre := m.MerkleRoot() + preJSON, err := m.MarshalJSON() + require.NoError(t, err) + var preSerialized bytes.Buffer + require.NoError(t, m.Serialize(&preSerialized)) + + require.NoError(t, m.SetMemoryRange(addr, r)) + v := m.GetWord(0) + require.Equal(t, Word(0), v) + post := m.MerkleRoot() + require.Equal(t, pre, post) + + // Assert that there are no extra zero pages in serialization + postJSON, err := m.MarshalJSON() + require.NoError(t, err) + require.Equal(t, preJSON, postJSON) + + var postSerialized bytes.Buffer + require.NoError(t, m.Serialize(&postSerialized)) + require.Equal(t, preSerialized.Bytes(), postSerialized.Bytes()) + }) + + t.Run("range page overlap", func(t *testing.T) { + m := NewMemory() + data := bytes.Repeat([]byte{0xAA}, PageAddrSize) + require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) + for i := 0; i < PageAddrSize/arch.WordSizeBytes; i++ { + addr := Word(i * arch.WordSizeBytes) + require.Equal(t, Word(0xAAAAAAAA), m.GetWord(addr)) + } + + data = []byte{0x11, 0x22} + require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) + require.Equal(t, Word(0x1122_AAAA), m.GetWord(0)) + for i := 1; i < PageAddrSize/arch.WordSizeBytes; i++ { + addr := Word(i * arch.WordSizeBytes) + require.Equal(t, Word(0xAAAAAAAA), m.GetWord(addr)) + } + }) + t.Run("read-write", func(t *testing.T) { m := NewMemory() m.SetWord(12, 0xAABBCCDD) diff --git a/cannon/mipsevm/tests/fuzz_evm_common64_test.go b/cannon/mipsevm/tests/fuzz_evm_common64_test.go index 89aceb41a4e6..d972c3a7ff26 100644 --- a/cannon/mipsevm/tests/fuzz_evm_common64_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_common64_test.go @@ -91,12 +91,6 @@ func FuzzStateConsistencyMultuOp(f *testing.F) { }) } -type insn struct { - opcode uint32 - expectRdReg bool - funct uint32 -} - func mulOpConsistencyCheck( t *testing.T, versions []VersionedVMTestCase, opcode uint32, expectRdReg bool, funct uint32, From 451224fe4a4f142e0d40512b05cfe537cdec91b9 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 30 Oct 2024 14:13:04 -0700 Subject: [PATCH 082/451] cannon: Update reference for stf-verify (#12752) --- cannon/Dockerfile.diff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cannon/Dockerfile.diff b/cannon/Dockerfile.diff index 6ed17520b702..a158ef9df79b 100644 --- a/cannon/Dockerfile.diff +++ b/cannon/Dockerfile.diff @@ -23,7 +23,7 @@ ARG GIT_DATE ARG TARGETOS TARGETARCH -FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.3 AS cannon-v2 +FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/cannon:v1.1.0-alpha.4 AS cannon-v2 FROM --platform=$BUILDPLATFORM builder as cannon-verify COPY --from=cannon-v2 /usr/local/bin/cannon /usr/local/bin/cannon-v2 From 4e13b20fe2f8b974089d73f565eb6ce6cc261c9b Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Wed, 30 Oct 2024 15:44:38 -0600 Subject: [PATCH 083/451] op-deployer: Reduce test code duplication, put test funcs first (#12751) --- .../deployer/integration_test/apply_test.go | 1092 ++++++++--------- 1 file changed, 506 insertions(+), 586 deletions(-) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index cf307c193908..af240aeffa73 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -14,10 +14,9 @@ import ( "testing" "time" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" - "github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/testutils/anvil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -156,336 +155,238 @@ func TestEndToEndApply(t *testing.T) { }) } -func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator { - _, testFilename, _, ok := runtime.Caller(0) - require.Truef(t, ok, "failed to get test filename") - monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") - artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) - require.NoError(t, err) - loc := &opcm.ArtifactsLocator{ - URL: artifactsURL, - } - return loc -} +func TestApplyExistingOPCM(t *testing.T) { + anvil.Test(t) -func createEnv( - t *testing.T, - ctx context.Context, - lgr log.Logger, - l1Client *ethclient.Client, - bcaster broadcaster.Broadcaster, - deployerAddr common.Address, -) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) { - _, testFilename, _, ok := runtime.Caller(0) - require.Truef(t, ok, "failed to get test filename") - monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") - artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) - require.NoError(t, err) - artifactsLocator := &opcm.ArtifactsLocator{ - URL: artifactsURL, + forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL") + if forkRPCUrl == "" { + t.Skip("no fork RPC URL provided") } - artifactsFS, cleanupArtifacts, err := pipeline.DownloadArtifacts(ctx, artifactsLocator, pipeline.NoopDownloadProgressor) - require.NoError(t, err) - defer func() { - require.NoError(t, cleanupArtifacts()) - }() + lgr := testlog.Logger(t, slog.LevelDebug) - host, err := pipeline.DefaultScriptHost( - bcaster, + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + runner, err := anvil.New( + forkRPCUrl, lgr, - deployerAddr, - artifactsFS, - 0, ) require.NoError(t, err) - env := &pipeline.Env{ - StateWriter: pipeline.NoopStateWriter(), - L1ScriptHost: host, - L1Client: l1Client, - Broadcaster: bcaster, - Deployer: deployerAddr, - Logger: lgr, - } + require.NoError(t, runner.Start(ctx)) + t.Cleanup(func() { + require.NoError(t, runner.Stop()) + }) - bundle := pipeline.ArtifactsBundle{ - L1: artifactsFS, - L2: artifactsFS, - } + l1Client, err := ethclient.Dial(runner.RPCUrl()) + require.NoError(t, err) - return env, bundle, host -} + l1ChainID := big.NewInt(11155111) + dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + require.NoError(t, err) + // index 0 from Anvil's test set + priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") + require.NoError(t, err) + signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID)) + deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") -func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address { - addr, err := dk.Address(key) + l2ChainID := uint256.NewInt(1) + + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: lgr, + ChainID: l1ChainID, + Client: l1Client, + Signer: signer, + From: deployerAddr, + }) require.NoError(t, err) - return addr -} -func newIntent( - t *testing.T, - l1ChainID *big.Int, - dk *devkeys.MnemonicDevKeys, - l2ChainID *uint256.Int, - l1Loc *opcm.ArtifactsLocator, - l2Loc *opcm.ArtifactsLocator, -) (*state.Intent, *state.State) { - intent := &state.Intent{ - DeploymentStrategy: state.DeploymentStrategyLive, - L1ChainID: l1ChainID.Uint64(), - SuperchainRoles: &state.SuperchainRoles{ - ProxyAdminOwner: addrFor(t, dk, devkeys.L1ProxyAdminOwnerRole.Key(l1ChainID)), - ProtocolVersionsOwner: addrFor(t, dk, devkeys.SuperchainDeployerKey.Key(l1ChainID)), - Guardian: addrFor(t, dk, devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)), - }, - FundDevAccounts: true, - L1ContractsLocator: l1Loc, - L2ContractsLocator: l2Loc, - Chains: []*state.ChainIntent{ - newChainIntent(t, dk, l1ChainID, l2ChainID), - }, - } - st := &state.State{ - Version: 1, - } - return intent, st -} + env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) -func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.Int, l2ChainID *uint256.Int) *state.ChainIntent { - return &state.ChainIntent{ - ID: l2ChainID.Bytes32(), - BaseFeeVaultRecipient: addrFor(t, dk, devkeys.BaseFeeVaultRecipientRole.Key(l1ChainID)), - L1FeeVaultRecipient: addrFor(t, dk, devkeys.L1FeeVaultRecipientRole.Key(l1ChainID)), - SequencerFeeVaultRecipient: addrFor(t, dk, devkeys.SequencerFeeVaultRecipientRole.Key(l1ChainID)), - Eip1559Denominator: 50, - Eip1559Elasticity: 6, - Roles: state.ChainRoles{ - L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), - UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), - Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), - Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), - Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), - }, - } -} + intent, st := newIntent( + t, + l1ChainID, + dk, + l2ChainID, + opcm.DefaultL1ContractsLocator, + opcm.DefaultL2ContractsLocator, + ) -type codeGetter func(t *testing.T, addr common.Address) []byte + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) -func ethClientCodeGetter(ctx context.Context, client *ethclient.Client) codeGetter { - return func(t *testing.T, addr common.Address) []byte { - code, err := client.CodeAt(ctx, addr, nil) - require.NoError(t, err) - return code - } + validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) } -func stateDumpCodeGetter(st *state.State) codeGetter { - return func(t *testing.T, addr common.Address) []byte { - acc, ok := st.L1StateDump.Data.Accounts[addr] - require.True(t, ok, "no account found for address %s", addr) - return acc.Code +func TestL2BlockTimeOverride(t *testing.T) { + op_e2e.InitParallel(t) + kurtosisutil.Test(t) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + env, bundle, intent, st := setupGenesisChain(t) + intent.GlobalDeployOverrides = map[string]interface{}{ + "l2BlockTime": float64(3), } + + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) + + cfg, err := state.CombineDeployConfig(intent, intent.Chains[0], st, st.Chains[0]) + require.NoError(t, err) + require.Equal(t, uint64(3), cfg.L2InitializationConfig.L2CoreDeployConfig.L2BlockTime, "L2 block time should be 3 seconds") } -func validateSuperchainDeployment(t *testing.T, st *state.State, cg codeGetter) { - addrs := []struct { - name string - addr common.Address - }{ - {"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress}, - {"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress}, - {"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress}, - {"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress}, - {"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress}, - {"OpcmProxy", st.ImplementationsDeployment.OpcmProxyAddress}, - {"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, - {"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress}, - } - for _, addr := range addrs { - t.Run(addr.name, func(t *testing.T) { - code := cg(t, addr.addr) - require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr) +func TestApplyGenesisStrategy(t *testing.T) { + op_e2e.InitParallel(t) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + env, bundle, intent, st := setupGenesisChain(t) + intent.DeploymentStrategy = state.DeploymentStrategyGenesis + + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) + + cg := stateDumpCodeGetter(st) + validateSuperchainDeployment(t, st, cg) + + for i := range intent.Chains { + t.Run(fmt.Sprintf("chain-%d", i), func(t *testing.T) { + validateOPChainDeployment(t, cg, st, intent) }) } } -func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, intent *state.Intent) { - // Validate that the implementation addresses are always set, even in subsequent deployments - // that pull from an existing OPCM deployment. - implAddrs := []struct { - name string - addr common.Address - }{ - {"DelayedWETHImplAddress", st.ImplementationsDeployment.DelayedWETHImplAddress}, - {"OptimismPortalImplAddress", st.ImplementationsDeployment.OptimismPortalImplAddress}, - {"SystemConfigImplAddress", st.ImplementationsDeployment.SystemConfigImplAddress}, - {"L1CrossDomainMessengerImplAddress", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress}, - {"L1ERC721BridgeImplAddress", st.ImplementationsDeployment.L1ERC721BridgeImplAddress}, - {"L1StandardBridgeImplAddress", st.ImplementationsDeployment.L1StandardBridgeImplAddress}, - {"OptimismMintableERC20FactoryImplAddress", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress}, - {"DisputeGameFactoryImplAddress", st.ImplementationsDeployment.DisputeGameFactoryImplAddress}, - {"MipsSingletonAddress", st.ImplementationsDeployment.MipsSingletonAddress}, - {"PreimageOracleSingletonAddress", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, - } - for _, addr := range implAddrs { - require.NotEmpty(t, addr.addr, "%s should be set", addr.name) - code := cg(t, addr.addr) - require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr) - } +func TestProofParamOverrides(t *testing.T) { + op_e2e.InitParallel(t) - for i, chainState := range st.Chains { - chainAddrs := []struct { - name string - addr common.Address - }{ - {"ProxyAdminAddress", chainState.ProxyAdminAddress}, - {"AddressManagerAddress", chainState.AddressManagerAddress}, - {"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress}, - {"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress}, - {"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress}, - {"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress}, - {"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress}, - {"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress}, - {"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress}, - {"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress}, - {"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress}, - {"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress}, - {"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress}, - // {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress}, - } - for _, addr := range chainAddrs { - // TODO Delete this `if`` block once FaultDisputeGameAddress is deployed. - if addr.name == "FaultDisputeGameAddress" { - continue - } - code := cg(t, addr.addr) - require.NotEmpty(t, code, "contract %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID) - } - - alloc := chainState.Allocs.Data.Accounts - - chainIntent := intent.Chains[i] - checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) - - // ownership slots - var addrAsSlot common.Hash - addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes()) - // slot 0 - ownerSlot := common.Hash{} - checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot) - var defaultGovOwner common.Hash - defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes()) - checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - require.Equal(t, int(chainIntent.Eip1559Denominator), 50, "EIP1559Denominator should be set") - require.Equal(t, int(chainIntent.Eip1559Elasticity), 6, "EIP1559Elasticity should be set") + env, bundle, intent, st := setupGenesisChain(t) + intent.GlobalDeployOverrides = map[string]any{ + "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, + "minProposalSizeBytes": standard.MinProposalSizeBytes + 1, + "challengePeriodSeconds": standard.ChallengePeriodSeconds + 1, + "proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1, + "disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1, + "mipsVersion": standard.MIPSVersion + 1, + "disputeGameType": standard.DisputeGameType, // This must be set to the permissioned game + "disputeAbsolutePrestate": common.Hash{'A', 'B', 'S', 'O', 'L', 'U', 'T', 'E'}, + "disputeMaxGameDepth": standard.DisputeMaxGameDepth + 1, + "disputeSplitDepth": standard.DisputeSplitDepth + 1, + "disputeClockExtension": standard.DisputeClockExtension + 1, + "disputeMaxClockDuration": standard.DisputeMaxClockDuration + 1, + "dangerouslyAllowCustomDisputeParameters": true, } -} - -func getEIP1967ImplementationAddress(t *testing.T, allocations types.GenesisAlloc, proxyAddress common.Address) common.Address { - storage := allocations[proxyAddress].Storage - storageValue := storage[genesis.ImplementationSlot] - require.NotEmpty(t, storageValue, "Implementation address for %s should be set", proxyAddress) - return common.HexToAddress(storageValue.Hex()) -} - -type bytesMarshaler interface { - Bytes() []byte -} -func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { - implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) - checkImmutable(t, allocations, implementationAddress, thing) -} + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) -func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { - account, ok := allocations[implementationAddress] - require.True(t, ok, "%s not found in allocations", implementationAddress) - require.NotEmpty(t, account.Code, "%s should have code", implementationAddress) - require.True( - t, - bytes.Contains(account.Code, thing.Bytes()), - "%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()), - ) -} + allocs := st.L1StateDump.Data.Accounts + chainState := st.Chains[0] -func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) { - account, ok := allocs[address] - require.True(t, ok, "account not found for address %s", address) - value, ok := account.Storage[slot] - if expected == (common.Hash{}) { - require.False(t, ok, "slot %s for account %s should not be set", slot, address) - return + uint64Caster := func(t *testing.T, val any) common.Hash { + return common.BigToHash(new(big.Int).SetUint64(val.(uint64))) } - require.True(t, ok, "slot %s not found for account %s", slot, address) - require.Equal(t, expected, value, "slot %s for account %s should be %s", slot, address, expected) -} - -func TestApplyExistingOPCM(t *testing.T) { - anvil.Test(t) - forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL") - if forkRPCUrl == "" { - t.Skip("no fork RPC URL provided") + tests := []struct { + name string + caster func(t *testing.T, val any) common.Hash + address common.Address + }{ + { + "withdrawalDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.DelayedWETHImplAddress, + }, + { + "minProposalSizeBytes", + uint64Caster, + st.ImplementationsDeployment.PreimageOracleSingletonAddress, + }, + { + "challengePeriodSeconds", + uint64Caster, + st.ImplementationsDeployment.PreimageOracleSingletonAddress, + }, + { + "proofMaturityDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.OptimismPortalImplAddress, + }, + { + "disputeGameFinalityDelaySeconds", + uint64Caster, + st.ImplementationsDeployment.OptimismPortalImplAddress, + }, + { + "disputeAbsolutePrestate", + func(t *testing.T, val any) common.Hash { + return val.(common.Hash) + }, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeMaxGameDepth", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeSplitDepth", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeClockExtension", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + { + "disputeMaxClockDuration", + uint64Caster, + chainState.PermissionedDisputeGameAddress, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) + }) } +} - lgr := testlog.Logger(t, slog.LevelDebug) +func TestInteropDeployment(t *testing.T) { + op_e2e.InitParallel(t) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - runner, err := anvil.New( - forkRPCUrl, - lgr, - ) - require.NoError(t, err) - - require.NoError(t, runner.Start(ctx)) - t.Cleanup(func() { - require.NoError(t, runner.Stop()) - }) - - l1Client, err := ethclient.Dial(runner.RPCUrl()) - require.NoError(t, err) - - l1ChainID := big.NewInt(11155111) - dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) - require.NoError(t, err) - // index 0 from Anvil's test set - priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") - require.NoError(t, err) - signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID)) - deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") - - l2ChainID := uint256.NewInt(1) - - bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ - Logger: lgr, - ChainID: l1ChainID, - Client: l1Client, - Signer: signer, - From: deployerAddr, - }) - require.NoError(t, err) - - env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) - - intent, st := newIntent( - t, - l1ChainID, - dk, - l2ChainID, - opcm.DefaultL1ContractsLocator, - opcm.DefaultL2ContractsLocator, - ) + env, bundle, intent, st := setupGenesisChain(t) + intent.UseInterop = true require.NoError(t, deployer.ApplyPipeline( ctx, @@ -495,253 +396,98 @@ func TestApplyExistingOPCM(t *testing.T) { st, )) - validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) + chainState := st.Chains[0] + depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") + checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) + proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes()) + checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) } -func TestL2BlockTimeOverride(t *testing.T) { +func TestInvalidL2Genesis(t *testing.T) { op_e2e.InitParallel(t) - kurtosisutil.Test(t) lgr := testlog.Logger(t, slog.LevelDebug) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - enclaveCtx := kurtosisutil.StartEnclave(t, ctx, lgr, "github.com/ethpandaops/ethereum-package", TestParams) - - service, err := enclaveCtx.GetServiceContext("el-1-geth-lighthouse") - require.NoError(t, err) - - ip := service.GetMaybePublicIPAddress() - ports := service.GetPublicPorts() - rpcURL := fmt.Sprintf("http://%s:%d", ip, ports["rpc"].GetNumber()) - l1Client, err := ethclient.Dial(rpcURL) - require.NoError(t, err) - depKey := new(deployerKey) l1ChainID := big.NewInt(77799777) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) require.NoError(t, err) - pk, err := dk.Secret(depKey) - require.NoError(t, err) - signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID)) - l2ChainID := uint256.NewInt(1) + l2ChainID1 := uint256.NewInt(1) deployerAddr, err := dk.Address(depKey) require.NoError(t, err) loc := localArtifactsLocator(t) - bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ - Logger: lgr, - ChainID: l1ChainID, - Client: l1Client, - Signer: signer, - From: deployerAddr, - }) - require.NoError(t, err) - - env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) - - intent, st := newIntent( - t, - l1ChainID, - dk, - l2ChainID, - loc, - loc, - ) - - intent.GlobalDeployOverrides = map[string]interface{}{ - "l2BlockTime": float64(3), - } - - require.NoError(t, deployer.ApplyPipeline( - ctx, - env, - bundle, - intent, - st, - )) - - chainIntent, err := intent.Chain(l2ChainID.Bytes32()) - require.NoError(t, err) - chainState, err := st.Chain(l2ChainID.Bytes32()) - require.NoError(t, err) - cfg, err := state.CombineDeployConfig(intent, chainIntent, st, chainState) - require.NoError(t, err) - - require.Equal(t, uint64(3), cfg.L2InitializationConfig.L2CoreDeployConfig.L2BlockTime, "L2 block time should be 3 seconds") -} - -func TestApplyGenesisStrategy(t *testing.T) { - op_e2e.InitParallel(t) - - lgr := testlog.Logger(t, slog.LevelDebug) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - depKey := new(deployerKey) - l1ChainID := big.NewInt(77799777) - dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) - require.NoError(t, err) - - l2ChainID1 := uint256.NewInt(1) - l2ChainID2 := uint256.NewInt(2) - - deployerAddr, err := dk.Address(depKey) - require.NoError(t, err) - - loc := localArtifactsLocator(t) - - env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) - intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) - intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2)) - intent.DeploymentStrategy = state.DeploymentStrategyGenesis - - require.NoError(t, deployer.ApplyPipeline( - ctx, - env, - bundle, - intent, - st, - )) - - cg := stateDumpCodeGetter(st) - validateSuperchainDeployment(t, st, cg) - - for i := range intent.Chains { - t.Run(fmt.Sprintf("chain-%d", i), func(t *testing.T) { - validateOPChainDeployment(t, cg, st, intent) - }) - } -} - -func TestProofParamOverrides(t *testing.T) { - op_e2e.InitParallel(t) - - lgr := testlog.Logger(t, slog.LevelDebug) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - depKey := new(deployerKey) - l1ChainID := big.NewInt(77799777) - dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) - require.NoError(t, err) - - l2ChainID1 := uint256.NewInt(1) - - deployerAddr, err := dk.Address(depKey) - require.NoError(t, err) - - loc := localArtifactsLocator(t) - - env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) - intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) - intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) - intent.DeploymentStrategy = state.DeploymentStrategyGenesis - intent.GlobalDeployOverrides = map[string]any{ - "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, - "minProposalSizeBytes": standard.MinProposalSizeBytes + 1, - "challengePeriodSeconds": standard.ChallengePeriodSeconds + 1, - "proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1, - "disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1, - "mipsVersion": standard.MIPSVersion + 1, - "disputeGameType": standard.DisputeGameType, // This must be set to the permissioned game - "disputeAbsolutePrestate": common.Hash{'A', 'B', 'S', 'O', 'L', 'U', 'T', 'E'}, - "disputeMaxGameDepth": standard.DisputeMaxGameDepth + 1, - "disputeSplitDepth": standard.DisputeSplitDepth + 1, - "disputeClockExtension": standard.DisputeClockExtension + 1, - "disputeMaxClockDuration": standard.DisputeMaxClockDuration + 1, - "dangerouslyAllowCustomDisputeParameters": true, - } - - require.NoError(t, deployer.ApplyPipeline( - ctx, - env, - bundle, - intent, - st, - )) - - allocs := st.L1StateDump.Data.Accounts - chainState := st.Chains[0] - - uint64Caster := func(t *testing.T, val any) common.Hash { - return common.BigToHash(new(big.Int).SetUint64(val.(uint64))) - } - + // these tests were generated by grepping all usages of the deploy + // config in L2Genesis.s.sol. tests := []struct { - name string - caster func(t *testing.T, val any) common.Hash - address common.Address + name string + overrides map[string]any }{ { - "withdrawalDelaySeconds", - uint64Caster, - st.ImplementationsDeployment.DelayedWETHImplAddress, - }, - { - "minProposalSizeBytes", - uint64Caster, - st.ImplementationsDeployment.PreimageOracleSingletonAddress, - }, - { - "challengePeriodSeconds", - uint64Caster, - st.ImplementationsDeployment.PreimageOracleSingletonAddress, - }, - { - "proofMaturityDelaySeconds", - uint64Caster, - st.ImplementationsDeployment.OptimismPortalImplAddress, - }, - { - "disputeGameFinalityDelaySeconds", - uint64Caster, - st.ImplementationsDeployment.OptimismPortalImplAddress, + name: "L2 proxy admin owner not set", + overrides: map[string]any{ + "proxyAdminOwner": nil, + }, }, { - "disputeAbsolutePrestate", - func(t *testing.T, val any) common.Hash { - return val.(common.Hash) + name: "base fee vault recipient not set", + overrides: map[string]any{ + "baseFeeVaultRecipient": nil, }, - chainState.PermissionedDisputeGameAddress, }, { - "disputeMaxGameDepth", - uint64Caster, - chainState.PermissionedDisputeGameAddress, + name: "l1 fee vault recipient not set", + overrides: map[string]any{ + "l1FeeVaultRecipient": nil, + }, }, { - "disputeSplitDepth", - uint64Caster, - chainState.PermissionedDisputeGameAddress, + name: "sequencer fee vault recipient not set", + overrides: map[string]any{ + "sequencerFeeVaultRecipient": nil, + }, }, { - "disputeClockExtension", - uint64Caster, - chainState.PermissionedDisputeGameAddress, + name: "l1 chain ID not set", + overrides: map[string]any{ + "l1ChainID": nil, + }, }, { - "disputeMaxClockDuration", - uint64Caster, - chainState.PermissionedDisputeGameAddress, + name: "l2 chain ID not set", + overrides: map[string]any{ + "l2ChainID": nil, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) + env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) + intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) + intent.DeploymentStrategy = state.DeploymentStrategyGenesis + intent.GlobalDeployOverrides = tt.overrides + + err := deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + ) + require.Error(t, err) + require.ErrorContains(t, err, "failed to combine L2 init config") }) } } -func TestInteropDeployment(t *testing.T) { - op_e2e.InitParallel(t) - +func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *state.Intent, *state.State) { lgr := testlog.Logger(t, slog.LevelDebug) ctx, cancel := context.WithCancel(context.Background()) @@ -763,104 +509,278 @@ func TestInteropDeployment(t *testing.T) { intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis - intent.UseInterop = true - - require.NoError(t, deployer.ApplyPipeline( - ctx, - env, - bundle, - intent, - st, - )) - - chainState := st.Chains[0] - depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") - checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) - proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes()) - checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) - + return env, bundle, intent, st } -func TestInvalidL2Genesis(t *testing.T) { - op_e2e.InitParallel(t) - - lgr := testlog.Logger(t, slog.LevelDebug) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - depKey := new(deployerKey) - l1ChainID := big.NewInt(77799777) - dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) +func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator { + _, testFilename, _, ok := runtime.Caller(0) + require.Truef(t, ok, "failed to get test filename") + monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") + artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") + artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) require.NoError(t, err) - - l2ChainID1 := uint256.NewInt(1) - - deployerAddr, err := dk.Address(depKey) + loc := &opcm.ArtifactsLocator{ + URL: artifactsURL, + } + return loc +} + +func createEnv( + t *testing.T, + ctx context.Context, + lgr log.Logger, + l1Client *ethclient.Client, + bcaster broadcaster.Broadcaster, + deployerAddr common.Address, +) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) { + _, testFilename, _, ok := runtime.Caller(0) + require.Truef(t, ok, "failed to get test filename") + monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") + artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") + artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) require.NoError(t, err) + artifactsLocator := &opcm.ArtifactsLocator{ + URL: artifactsURL, + } - loc := localArtifactsLocator(t) + artifactsFS, cleanupArtifacts, err := pipeline.DownloadArtifacts(ctx, artifactsLocator, pipeline.NoopDownloadProgressor) + require.NoError(t, err) + defer func() { + require.NoError(t, cleanupArtifacts()) + }() - // these tests were generated by grepping all usages of the deploy - // config in L2Genesis.s.sol. - tests := []struct { - name string - overrides map[string]any - }{ - { - name: "L2 proxy admin owner not set", - overrides: map[string]any{ - "proxyAdminOwner": nil, - }, - }, - { - name: "base fee vault recipient not set", - overrides: map[string]any{ - "baseFeeVaultRecipient": nil, - }, - }, - { - name: "l1 fee vault recipient not set", - overrides: map[string]any{ - "l1FeeVaultRecipient": nil, - }, - }, - { - name: "sequencer fee vault recipient not set", - overrides: map[string]any{ - "sequencerFeeVaultRecipient": nil, - }, + host, err := pipeline.DefaultScriptHost( + bcaster, + lgr, + deployerAddr, + artifactsFS, + 0, + ) + require.NoError(t, err) + + env := &pipeline.Env{ + StateWriter: pipeline.NoopStateWriter(), + L1ScriptHost: host, + L1Client: l1Client, + Broadcaster: bcaster, + Deployer: deployerAddr, + Logger: lgr, + } + + bundle := pipeline.ArtifactsBundle{ + L1: artifactsFS, + L2: artifactsFS, + } + + return env, bundle, host +} + +func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address { + addr, err := dk.Address(key) + require.NoError(t, err) + return addr +} + +func newIntent( + t *testing.T, + l1ChainID *big.Int, + dk *devkeys.MnemonicDevKeys, + l2ChainID *uint256.Int, + l1Loc *opcm.ArtifactsLocator, + l2Loc *opcm.ArtifactsLocator, +) (*state.Intent, *state.State) { + intent := &state.Intent{ + DeploymentStrategy: state.DeploymentStrategyLive, + L1ChainID: l1ChainID.Uint64(), + SuperchainRoles: &state.SuperchainRoles{ + ProxyAdminOwner: addrFor(t, dk, devkeys.L1ProxyAdminOwnerRole.Key(l1ChainID)), + ProtocolVersionsOwner: addrFor(t, dk, devkeys.SuperchainDeployerKey.Key(l1ChainID)), + Guardian: addrFor(t, dk, devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)), }, - { - name: "l1 chain ID not set", - overrides: map[string]any{ - "l1ChainID": nil, - }, + FundDevAccounts: true, + L1ContractsLocator: l1Loc, + L2ContractsLocator: l2Loc, + Chains: []*state.ChainIntent{ + newChainIntent(t, dk, l1ChainID, l2ChainID), }, - { - name: "l2 chain ID not set", - overrides: map[string]any{ - "l2ChainID": nil, - }, + } + st := &state.State{ + Version: 1, + } + return intent, st +} + +func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.Int, l2ChainID *uint256.Int) *state.ChainIntent { + return &state.ChainIntent{ + ID: l2ChainID.Bytes32(), + BaseFeeVaultRecipient: addrFor(t, dk, devkeys.BaseFeeVaultRecipientRole.Key(l1ChainID)), + L1FeeVaultRecipient: addrFor(t, dk, devkeys.L1FeeVaultRecipientRole.Key(l1ChainID)), + SequencerFeeVaultRecipient: addrFor(t, dk, devkeys.SequencerFeeVaultRecipientRole.Key(l1ChainID)), + Eip1559Denominator: 50, + Eip1559Elasticity: 6, + Roles: state.ChainRoles{ + L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), + UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), + Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), + Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), + Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) - intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) - intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) - intent.DeploymentStrategy = state.DeploymentStrategyGenesis - intent.GlobalDeployOverrides = tt.overrides +} - err := deployer.ApplyPipeline( - ctx, - env, - bundle, - intent, - st, - ) - require.Error(t, err) - require.ErrorContains(t, err, "failed to combine L2 init config") +type codeGetter func(t *testing.T, addr common.Address) []byte + +func ethClientCodeGetter(ctx context.Context, client *ethclient.Client) codeGetter { + return func(t *testing.T, addr common.Address) []byte { + code, err := client.CodeAt(ctx, addr, nil) + require.NoError(t, err) + return code + } +} + +func stateDumpCodeGetter(st *state.State) codeGetter { + return func(t *testing.T, addr common.Address) []byte { + acc, ok := st.L1StateDump.Data.Accounts[addr] + require.True(t, ok, "no account found for address %s", addr) + return acc.Code + } +} + +func validateSuperchainDeployment(t *testing.T, st *state.State, cg codeGetter) { + addrs := []struct { + name string + addr common.Address + }{ + {"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress}, + {"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress}, + {"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress}, + {"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress}, + {"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress}, + {"OpcmProxy", st.ImplementationsDeployment.OpcmProxyAddress}, + {"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, + {"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress}, + } + for _, addr := range addrs { + t.Run(addr.name, func(t *testing.T) { + code := cg(t, addr.addr) + require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr) }) } } + +func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, intent *state.Intent) { + // Validate that the implementation addresses are always set, even in subsequent deployments + // that pull from an existing OPCM deployment. + implAddrs := []struct { + name string + addr common.Address + }{ + {"DelayedWETHImplAddress", st.ImplementationsDeployment.DelayedWETHImplAddress}, + {"OptimismPortalImplAddress", st.ImplementationsDeployment.OptimismPortalImplAddress}, + {"SystemConfigImplAddress", st.ImplementationsDeployment.SystemConfigImplAddress}, + {"L1CrossDomainMessengerImplAddress", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress}, + {"L1ERC721BridgeImplAddress", st.ImplementationsDeployment.L1ERC721BridgeImplAddress}, + {"L1StandardBridgeImplAddress", st.ImplementationsDeployment.L1StandardBridgeImplAddress}, + {"OptimismMintableERC20FactoryImplAddress", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress}, + {"DisputeGameFactoryImplAddress", st.ImplementationsDeployment.DisputeGameFactoryImplAddress}, + {"MipsSingletonAddress", st.ImplementationsDeployment.MipsSingletonAddress}, + {"PreimageOracleSingletonAddress", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, + } + for _, addr := range implAddrs { + require.NotEmpty(t, addr.addr, "%s should be set", addr.name) + code := cg(t, addr.addr) + require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr) + } + + for i, chainState := range st.Chains { + chainAddrs := []struct { + name string + addr common.Address + }{ + {"ProxyAdminAddress", chainState.ProxyAdminAddress}, + {"AddressManagerAddress", chainState.AddressManagerAddress}, + {"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress}, + {"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress}, + {"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress}, + {"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress}, + {"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress}, + {"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress}, + {"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress}, + {"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress}, + {"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress}, + {"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress}, + {"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress}, + // {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress}, + } + for _, addr := range chainAddrs { + // TODO Delete this `if`` block once FaultDisputeGameAddress is deployed. + if addr.name == "FaultDisputeGameAddress" { + continue + } + code := cg(t, addr.addr) + require.NotEmpty(t, code, "contract %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID) + } + + alloc := chainState.Allocs.Data.Accounts + + chainIntent := intent.Chains[i] + checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) + + // ownership slots + var addrAsSlot common.Hash + addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes()) + // slot 0 + ownerSlot := common.Hash{} + checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot) + var defaultGovOwner common.Hash + defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes()) + checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner) + + require.Equal(t, int(chainIntent.Eip1559Denominator), 50, "EIP1559Denominator should be set") + require.Equal(t, int(chainIntent.Eip1559Elasticity), 6, "EIP1559Elasticity should be set") + } +} + +func getEIP1967ImplementationAddress(t *testing.T, allocations types.GenesisAlloc, proxyAddress common.Address) common.Address { + storage := allocations[proxyAddress].Storage + storageValue := storage[genesis.ImplementationSlot] + require.NotEmpty(t, storageValue, "Implementation address for %s should be set", proxyAddress) + return common.HexToAddress(storageValue.Hex()) +} + +type bytesMarshaler interface { + Bytes() []byte +} + +func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { + implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) + checkImmutable(t, allocations, implementationAddress, thing) +} + +func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { + account, ok := allocations[implementationAddress] + require.True(t, ok, "%s not found in allocations", implementationAddress) + require.NotEmpty(t, account.Code, "%s should have code", implementationAddress) + require.True( + t, + bytes.Contains(account.Code, thing.Bytes()), + "%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()), + ) +} + +func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) { + account, ok := allocs[address] + require.True(t, ok, "account not found for address %s", address) + value, ok := account.Storage[slot] + if expected == (common.Hash{}) { + require.False(t, ok, "slot %s for account %s should not be set", slot, address) + return + } + require.True(t, ok, "slot %s not found for account %s", slot, address) + require.Equal(t, expected, value, "slot %s for account %s should be %s", slot, address, expected) +} From 630f8466d276c4a248ac90b4f5b45dd7f86b0b1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:32:02 +0700 Subject: [PATCH 084/451] dependabot(gomod): bump github.com/minio/minio-go/v7 (#12749) Bumps [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) from 7.0.79 to 7.0.80. - [Release notes](https://github.com/minio/minio-go/releases) - [Commits](https://github.com/minio/minio-go/compare/v7.0.79...v7.0.80) --- updated-dependencies: - dependency-name: github.com/minio/minio-go/v7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 758d076028e8..2d0cf6d664d9 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/mattn/go-isatty v0.0.20 - github.com/minio/minio-go/v7 v7.0.79 + github.com/minio/minio-go/v7 v7.0.80 github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-multiaddr v0.13.0 github.com/multiformats/go-multiaddr-dns v0.4.0 diff --git a/go.sum b/go.sum index a2dc3af16462..fb1bc5479f4d 100644 --- a/go.sum +++ b/go.sum @@ -524,8 +524,8 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4S github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.79 h1:SvJZpj3hT0RN+4KiuX/FxLfPZdsuegy6d/2PiemM/bM= -github.com/minio/minio-go/v7 v7.0.79/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk= +github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= From 47da14af2d4fc75fcf9cac9d8dbb38f64d1b8e46 Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Thu, 31 Oct 2024 08:06:26 +0100 Subject: [PATCH 085/451] op-node/rollup: Implement Holocene invalid payload attributes handling (#12621) * op-node/rollup: Implement Holocene invalid payload handling * op-node: update comment about block-processing errors --------- Co-authored-by: protolambda --- op-e2e/actions/helpers/env.go | 39 +++++++- op-e2e/actions/helpers/l2_batcher.go | 4 +- op-e2e/actions/proofs/bad_tx_in_batch_test.go | 3 +- op-e2e/actions/upgrades/holocene_fork_test.go | 89 ++++++++++++++++++- op-node/rollup/attributes/attributes.go | 3 +- op-node/rollup/derive/attributes_queue.go | 55 ++++++++++-- op-node/rollup/derive/batch_queue.go | 10 ++- op-node/rollup/derive/channel_assembler.go | 4 + op-node/rollup/derive/channel_bank.go | 6 ++ op-node/rollup/derive/channel_in_reader.go | 3 +- op-node/rollup/derive/deriver.go | 29 +++++- op-node/rollup/derive/pipeline.go | 21 +++-- op-node/rollup/engine/build_invalid.go | 27 +++--- op-node/rollup/engine/events.go | 19 ++-- op-node/rollup/engine/payload_process.go | 16 +++- op-service/eth/types.go | 25 ++++++ 16 files changed, 303 insertions(+), 50 deletions(-) diff --git a/op-e2e/actions/helpers/env.go b/op-e2e/actions/helpers/env.go index 1bc270f4843f..77911aa80bf5 100644 --- a/op-e2e/actions/helpers/env.go +++ b/op-e2e/actions/helpers/env.go @@ -1,11 +1,14 @@ package helpers import ( + "math/rand" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + e2ecfg "github.com/ethereum-optimism/optimism/op-e2e/config" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/sync" @@ -16,7 +19,8 @@ type Env struct { Log log.Logger Logs *testlog.CapturingHandler - SetupData *e2eutils.SetupData + DeployParams *e2eutils.DeployParams + SetupData *e2eutils.SetupData Miner *L1Miner Seq *L2Sequencer @@ -24,6 +28,9 @@ type Env struct { Verifier *L2Verifier VerifEngine *L2Engine Batcher *L2Batcher + Alice *CrossLayerUser + + AddressCorpora []common.Address } type EnvOpt struct { @@ -49,8 +56,9 @@ const DefaultFork = rollup.Holocene // SetupEnv sets up a default action test environment. If no fork is specified, the default fork as // specified by the package variable [defaultFork] is used. -func SetupEnv(t StatefulTesting, opts ...EnvOpt) (env Env) { +func SetupEnv(t Testing, opts ...EnvOpt) (env Env) { dp := e2eutils.MakeDeployParams(t, DefaultRollupTestParams()) + env.DeployParams = dp log, logs := testlog.CaptureLogger(t, log.LevelDebug) env.Log, env.Logs = log, logs @@ -64,6 +72,8 @@ func SetupEnv(t StatefulTesting, opts ...EnvOpt) (env Env) { sd := e2eutils.Setup(t, dp, DefaultAlloc) env.SetupData = sd + env.AddressCorpora = e2eutils.CollectAddresses(sd, dp) + env.Miner, env.SeqEngine, env.Seq = SetupSequencerTest(t, sd, log) env.Miner.ActL1SetFeeRecipient(common.Address{'A'}) env.VerifEngine, env.Verifier = SetupVerifier(t, sd, log, env.Miner.L1Client(t, sd.RollupCfg), env.Miner.BlobStore(), &sync.Config{}) @@ -71,9 +81,34 @@ func SetupEnv(t StatefulTesting, opts ...EnvOpt) (env Env) { env.Batcher = NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), rollupSeqCl, env.Miner.EthClient(), env.SeqEngine.EthClient(), env.SeqEngine.EngineClient(t, sd.RollupCfg)) + alice := NewCrossLayerUser(log, dp.Secrets.Alice, rand.New(rand.NewSource(0xa57b)), e2ecfg.AllocTypeStandard) + alice.L1.SetUserEnv(env.L1UserEnv(t)) + alice.L2.SetUserEnv(env.L2UserEnv(t)) + env.Alice = alice + return } +func (env Env) L1UserEnv(t Testing) *BasicUserEnv[*L1Bindings] { + l1EthCl := env.Miner.EthClient() + return &BasicUserEnv[*L1Bindings]{ + EthCl: l1EthCl, + Signer: types.LatestSigner(env.SetupData.L1Cfg.Config), + AddressCorpora: env.AddressCorpora, + Bindings: NewL1Bindings(t, l1EthCl, e2ecfg.AllocTypeStandard), + } +} + +func (env Env) L2UserEnv(t Testing) *BasicUserEnv[*L2Bindings] { + l2EthCl := env.SeqEngine.EthClient() + return &BasicUserEnv[*L2Bindings]{ + EthCl: l2EthCl, + Signer: types.LatestSigner(env.SetupData.L2Cfg.Config), + AddressCorpora: env.AddressCorpora, + Bindings: NewL2Bindings(t, l2EthCl, env.SeqEngine.GethClient()), + } +} + func (env Env) ActBatchSubmitAllAndMine(t Testing) (l1InclusionBlock *types.Block) { env.Batcher.ActSubmitAll(t) batchTx := env.Batcher.LastSubmitted diff --git a/op-e2e/actions/helpers/l2_batcher.go b/op-e2e/actions/helpers/l2_batcher.go index a106c5687663..a2121f77ca54 100644 --- a/op-e2e/actions/helpers/l2_batcher.go +++ b/op-e2e/actions/helpers/l2_batcher.go @@ -146,8 +146,8 @@ func (s *L2Batcher) Reset() { // ActL2BatchBuffer adds the next L2 block to the batch buffer. // If the buffer is being submitted, the buffer is wiped. -func (s *L2Batcher) ActL2BatchBuffer(t Testing) { - require.NoError(t, s.Buffer(t), "failed to add block to channel") +func (s *L2Batcher) ActL2BatchBuffer(t Testing, opts ...BlockModifier) { + require.NoError(t, s.Buffer(t, opts...), "failed to add block to channel") } type BlockModifier = func(block *types.Block) diff --git a/op-e2e/actions/proofs/bad_tx_in_batch_test.go b/op-e2e/actions/proofs/bad_tx_in_batch_test.go index 6aed725f2be8..b750e8f9e649 100644 --- a/op-e2e/actions/proofs/bad_tx_in_batch_test.go +++ b/op-e2e/actions/proofs/bad_tx_in_batch_test.go @@ -26,14 +26,13 @@ func runBadTxInBatchTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { env.Alice.L2.ActCheckReceiptStatusOfLastTx(true)(t) // Instruct the batcher to submit a faulty channel, with an invalid tx. - err := env.Batcher.Buffer(t, func(block *types.Block) { + env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) { // Replace the tx with one that has a bad signature. txs := block.Transactions() newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) txs[1] = newTx require.NoError(t, err) }) - require.NoError(t, err) env.Batcher.ActL2ChannelClose(t) env.Batcher.ActL2BatchSubmit(t) diff --git a/op-e2e/actions/upgrades/holocene_fork_test.go b/op-e2e/actions/upgrades/holocene_fork_test.go index d9145b3d50ff..a51059701d85 100644 --- a/op-e2e/actions/upgrades/holocene_fork_test.go +++ b/op-e2e/actions/upgrades/holocene_fork_test.go @@ -1,13 +1,18 @@ package upgrades import ( + "context" + "math/big" "testing" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/stretchr/testify/require" ) func TestHoloceneActivationAtGenesis(gt *testing.T) { @@ -127,3 +132,85 @@ func TestHoloceneLateActivationAndReset(gt *testing.T) { requirePreHoloceneActivationLogs(e2esys.RoleSeq, 4) requirePreHoloceneActivationLogs(e2esys.RoleVerif, 4) } + +func TestHoloceneInvalidPayload(gt *testing.T) { + t := helpers.NewDefaultTesting(gt) + env := helpers.SetupEnv(t, helpers.WithActiveGenesisFork(rollup.Holocene)) + ctx := context.Background() + + requireDepositOnlyLogs := func(role string, expNumLogs int) { + t.Helper() + recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter("deposits-only attributes"), testlog.NewAttributesFilter("role", role)) + require.Len(t, recs, expNumLogs) + } + + // Start op-nodes + env.Seq.ActL2PipelineFull(t) + + // generate and batch buffer two empty blocks + env.Seq.ActL2EmptyBlock(t) // 1 - genesis is 0 + env.Batcher.ActL2BatchBuffer(t) + env.Seq.ActL2EmptyBlock(t) // 2 + env.Batcher.ActL2BatchBuffer(t) + + // send and include a single transaction + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.DeployParams.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + + env.Seq.ActL2StartBlock(t) + env.SeqEngine.ActL2IncludeTx(env.Alice.Address())(t) + env.Seq.ActL2EndBlock(t) // 3 + env.Alice.L2.ActCheckReceiptStatusOfLastTx(true)(t) + l2Unsafe := env.Seq.L2Unsafe() + const invalidNum = 3 + require.EqualValues(t, invalidNum, l2Unsafe.Number) + b, err := env.SeqEngine.EthClient().BlockByNumber(ctx, big.NewInt(invalidNum)) + require.NoError(t, err) + require.Len(t, b.Transactions(), 2) + + // buffer into the batcher, invalidating the tx via signature zeroing + env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) { + // Replace the tx with one that has a bad signature. + txs := block.Transactions() + newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) + require.NoError(t, err) + txs[1] = newTx + }) + + // generate two more empty blocks + env.Seq.ActL2EmptyBlock(t) // 4 + env.Seq.ActL2EmptyBlock(t) // 5 + require.EqualValues(t, 5, env.Seq.L2Unsafe().Number) + + // submit it all + env.ActBatchSubmitAllAndMine(t) + + // derive chain on sequencer + env.Seq.ActL1HeadSignal(t) + env.Seq.ActL2PipelineFull(t) + + // TODO(12695): need to properly update safe after completed L1 block derivation + l2Safe := env.Seq.L2PendingSafe() + require.EqualValues(t, invalidNum, l2Safe.Number) + require.NotEqual(t, l2Safe.Hash, l2Unsafe.Hash, // old L2Unsafe above + "block-3 should have been replaced by deposit-only version") + requireDepositOnlyLogs(e2esys.RoleSeq, 2) + require.Equal(t, l2Safe, env.Seq.L2Unsafe(), "unsafe chain should have reorg'd") + b, err = env.SeqEngine.EthClient().BlockByNumber(ctx, big.NewInt(invalidNum)) + require.NoError(t, err) + require.Len(t, b.Transactions(), 1) + + // test that building on top of reorg'd chain and deriving further works + + env.Seq.ActL2EmptyBlock(t) // 4 + env.Seq.ActL2EmptyBlock(t) // 5 + l2Unsafe = env.Seq.L2Unsafe() + require.EqualValues(t, 5, l2Unsafe.Number) + + env.Batcher.Reset() // need to reset batcher to become aware of reorg + env.ActBatchSubmitAllAndMine(t) + env.Seq.ActL1HeadSignal(t) + env.Seq.ActL2PipelineFull(t) + require.Equal(t, l2Unsafe, env.Seq.L2Safe()) +} diff --git a/op-node/rollup/attributes/attributes.go b/op-node/rollup/attributes/attributes.go index 4ebb27050882..73876c0eaad8 100644 --- a/op-node/rollup/attributes/attributes.go +++ b/op-node/rollup/attributes/attributes.go @@ -62,6 +62,7 @@ func (eq *AttributesHandler) OnEvent(ev event.Event) bool { eq.onPendingSafeUpdate(x) case derive.DerivedAttributesEvent: eq.attributes = x.Attributes + eq.sentAttributes = false eq.emitter.Emit(derive.ConfirmReceivedAttributesEvent{}) // to make sure we have a pre-state signal to process the attributes from eq.emitter.Emit(engine.PendingSafeRequestEvent{}) @@ -71,7 +72,7 @@ func (eq *AttributesHandler) OnEvent(ev event.Event) bool { case rollup.EngineTemporaryErrorEvent: eq.sentAttributes = false case engine.InvalidPayloadAttributesEvent: - if x.Attributes.DerivedFrom == (eth.L1BlockRef{}) { + if !x.Attributes.IsDerived() { return true // from sequencing } eq.sentAttributes = false diff --git a/op-node/rollup/derive/attributes_queue.go b/op-node/rollup/derive/attributes_queue.go index 0111ba29c8e5..6823de4ed67d 100644 --- a/op-node/rollup/derive/attributes_queue.go +++ b/op-node/rollup/derive/attributes_queue.go @@ -2,6 +2,7 @@ package derive import ( "context" + "errors" "fmt" "io" "time" @@ -35,17 +36,32 @@ type AttributesWithParent struct { DerivedFrom eth.L1BlockRef } +// WithDepositsOnly return a shallow clone with all non-Deposit transactions +// stripped from the transactions of its attributes. The order is preserved. +func (a *AttributesWithParent) WithDepositsOnly() *AttributesWithParent { + clone := *a + clone.Attributes = clone.Attributes.WithDepositsOnly() + return &clone +} + +func (a *AttributesWithParent) IsDerived() bool { + return a.DerivedFrom != (eth.L1BlockRef{}) +} + type AttributesQueue struct { - log log.Logger - config *rollup.Config - builder AttributesBuilder - prev SingularBatchProvider + log log.Logger + config *rollup.Config + builder AttributesBuilder + prev SingularBatchProvider + batch *SingularBatch isLastInSpan bool + lastAttribs *AttributesWithParent } type SingularBatchProvider interface { ResettableStage + ChannelFlusher Origin() eth.L1BlockRef NextBatch(context.Context, eth.L2BlockRef) (*SingularBatch, bool, error) } @@ -85,11 +101,11 @@ func (aq *AttributesQueue) NextAttributes(ctx context.Context, parent eth.L2Bloc IsLastInSpan: aq.isLastInSpan, DerivedFrom: aq.Origin(), } + aq.lastAttribs = &attr aq.batch = nil aq.isLastInSpan = false return &attr, nil } - } // createNextAttributes transforms a batch into a payload attributes. This sets `NoTxPool` and appends the batched transactions @@ -120,8 +136,35 @@ func (aq *AttributesQueue) createNextAttributes(ctx context.Context, batch *Sing return attrs, nil } -func (aq *AttributesQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error { +func (aq *AttributesQueue) reset() { aq.batch = nil aq.isLastInSpan = false // overwritten later, but set for consistency + aq.lastAttribs = nil +} + +func (aq *AttributesQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error { + aq.reset() return io.EOF } + +func (aq *AttributesQueue) DepositsOnlyAttributes(parent eth.BlockID, derivedFrom eth.L1BlockRef) (*AttributesWithParent, error) { + // Sanity checks - these cannot happen with correct deriver implementations. + if aq.batch != nil { + return nil, fmt.Errorf("unexpected buffered batch, parent hash: %s, epoch: %s", aq.batch.ParentHash, aq.batch.Epoch()) + } else if aq.lastAttribs == nil { + return nil, errors.New("no attributes generated yet") + } else if derivedFrom != aq.lastAttribs.DerivedFrom { + return nil, fmt.Errorf( + "unexpected derivation origin, last_origin: %s, invalid_origin: %s", + aq.lastAttribs.DerivedFrom, derivedFrom) + } else if parent != aq.lastAttribs.Parent.ID() { + return nil, fmt.Errorf( + "unexpected parent: last_parent: %s, invalid_parent: %s", + aq.lastAttribs.Parent.ID(), parent) + } + + aq.prev.FlushChannel() // flush all channel data in previous stages + attrs := aq.lastAttribs.WithDepositsOnly() + aq.lastAttribs = attrs + return attrs, nil +} diff --git a/op-node/rollup/derive/batch_queue.go b/op-node/rollup/derive/batch_queue.go index 25199297144e..3029d1199767 100644 --- a/op-node/rollup/derive/batch_queue.go +++ b/op-node/rollup/derive/batch_queue.go @@ -27,10 +27,6 @@ import ( // It is internally responsible for making sure that batches with L1 inclusions block outside it's // working range are not considered or pruned. -type ChannelFlusher interface { - FlushChannel() -} - type NextBatchProvider interface { ChannelFlusher Origin() eth.L1BlockRef @@ -271,6 +267,12 @@ func (bq *BatchQueue) Reset(_ context.Context, base eth.L1BlockRef, _ eth.System return io.EOF } +func (bq *BatchQueue) FlushChannel() { + // We need to implement the ChannelFlusher interface with the BatchQueue but it's never called + // of which the BatchMux takes care. + panic("BatchQueue: invalid FlushChannel call") +} + func (bq *BatchQueue) AddBatch(ctx context.Context, batch Batch, parent eth.L2BlockRef) { if len(bq.l1Blocks) == 0 { panic(fmt.Errorf("cannot add batch with timestamp %d, no origin was prepared", batch.GetTimestamp())) diff --git a/op-node/rollup/derive/channel_assembler.go b/op-node/rollup/derive/channel_assembler.go index 6252bd217b71..be821ca955f4 100644 --- a/op-node/rollup/derive/channel_assembler.go +++ b/op-node/rollup/derive/channel_assembler.go @@ -51,6 +51,10 @@ func (ca *ChannelAssembler) Reset(context.Context, eth.L1BlockRef, eth.SystemCon return io.EOF } +func (ca *ChannelAssembler) FlushChannel() { + ca.resetChannel() +} + func (ca *ChannelAssembler) resetChannel() { ca.channel = nil } diff --git a/op-node/rollup/derive/channel_bank.go b/op-node/rollup/derive/channel_bank.go index f26cb34b7fcf..57ab70096507 100644 --- a/op-node/rollup/derive/channel_bank.go +++ b/op-node/rollup/derive/channel_bank.go @@ -203,6 +203,12 @@ func (cb *ChannelBank) Reset(ctx context.Context, base eth.L1BlockRef, _ eth.Sys return io.EOF } +func (bq *ChannelBank) FlushChannel() { + // We need to implement the ChannelFlusher interface with the ChannelBank but it's never called + // of which the ChannelMux takes care. + panic("ChannelBank: invalid FlushChannel call") +} + type L1BlockRefByHashFetcher interface { L1BlockRefByHash(context.Context, common.Hash) (eth.L1BlockRef, error) } diff --git a/op-node/rollup/derive/channel_in_reader.go b/op-node/rollup/derive/channel_in_reader.go index 6310fba600c9..cdb648c3b870 100644 --- a/op-node/rollup/derive/channel_in_reader.go +++ b/op-node/rollup/derive/channel_in_reader.go @@ -32,6 +32,7 @@ var ( type RawChannelProvider interface { ResettableStage + ChannelFlusher Origin() eth.L1BlockRef NextRawChannel(ctx context.Context) ([]byte, error) } @@ -134,5 +135,5 @@ func (cr *ChannelInReader) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.Sy func (cr *ChannelInReader) FlushChannel() { cr.nextBatchFn = nil - // TODO(12157): cr.prev.FlushChannel() - when we do wiring with ChannelStage + cr.prev.FlushChannel() } diff --git a/op-node/rollup/derive/deriver.go b/op-node/rollup/derive/deriver.go index 4a897f4ee4ec..d95fdf3017f7 100644 --- a/op-node/rollup/derive/deriver.go +++ b/op-node/rollup/derive/deriver.go @@ -3,6 +3,7 @@ package derive import ( "context" "errors" + "fmt" "io" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -64,6 +65,18 @@ func (ev PipelineStepEvent) String() string { return "pipeline-step" } +// DepositsOnlyPayloadAttributesRequestEvent requests a deposits-only version of the attributes from +// the pipeline. It is sent by the engine deriver and received by the PipelineDeriver. +// This event got introduced with Holocene. +type DepositsOnlyPayloadAttributesRequestEvent struct { + Parent eth.BlockID + DerivedFrom eth.L1BlockRef +} + +func (ev DepositsOnlyPayloadAttributesRequestEvent) String() string { + return "deposits-only-payload-attributes-request" +} + type PipelineDeriver struct { pipeline *DerivationPipeline @@ -122,8 +135,7 @@ func (d *PipelineDeriver) OnEvent(ev event.Event) bool { d.emitter.Emit(rollup.EngineTemporaryErrorEvent{Err: err}) } else { if attrib != nil { - d.needAttributesConfirmation = true - d.emitter.Emit(DerivedAttributesEvent{Attributes: attrib}) + d.emitDerivedAttributesEvent(attrib) } else { d.emitter.Emit(DeriverMoreEvent{}) // continue with the next step if we can } @@ -132,8 +144,21 @@ func (d *PipelineDeriver) OnEvent(ev event.Event) bool { d.pipeline.ConfirmEngineReset() case ConfirmReceivedAttributesEvent: d.needAttributesConfirmation = false + case DepositsOnlyPayloadAttributesRequestEvent: + d.pipeline.log.Warn("Deriving deposits-only attributes", "origin", d.pipeline.Origin()) + attrib, err := d.pipeline.DepositsOnlyAttributes(x.Parent, x.DerivedFrom) + if err != nil { + d.emitter.Emit(rollup.CriticalErrorEvent{Err: fmt.Errorf("deriving deposits-only attributes: %w", err)}) + return true + } + d.emitDerivedAttributesEvent(attrib) default: return false } return true } + +func (d *PipelineDeriver) emitDerivedAttributesEvent(attrib *AttributesWithParent) { + d.needAttributesConfirmation = true + d.emitter.Emit(DerivedAttributesEvent{Attributes: attrib}) +} diff --git a/op-node/rollup/derive/pipeline.go b/op-node/rollup/derive/pipeline.go index 95189b35e100..e85366fd0f0e 100644 --- a/op-node/rollup/derive/pipeline.go +++ b/op-node/rollup/derive/pipeline.go @@ -38,6 +38,13 @@ type ResettableStage interface { Reset(ctx context.Context, base eth.L1BlockRef, baseCfg eth.SystemConfig) error } +// A ChannelFlusher flushes all internal state related to the current channel and then +// calls FlushChannel on the stage it owns. Note that this is in contrast to Reset, which +// is called by the owning Pipeline in a loop over all stages. +type ChannelFlusher interface { + FlushChannel() +} + type ForkTransformer interface { Transform(rollup.ForkName) } @@ -89,16 +96,16 @@ func NewDerivationPipeline(log log.Logger, rollupCfg *rollup.Config, l1Fetcher L dataSrc := NewDataSourceFactory(log, rollupCfg, l1Fetcher, l1Blobs, altDA) // auxiliary stage for L1Retrieval l1Src := NewL1Retrieval(log, dataSrc, l1Traversal) frameQueue := NewFrameQueue(log, rollupCfg, l1Src) - bank := NewChannelMux(log, spec, frameQueue, metrics) - chInReader := NewChannelInReader(rollupCfg, log, bank, metrics) - batchQueue := NewBatchMux(log, rollupCfg, chInReader, l2Source) + channelMux := NewChannelMux(log, spec, frameQueue, metrics) + chInReader := NewChannelInReader(rollupCfg, log, channelMux, metrics) + batchMux := NewBatchMux(log, rollupCfg, chInReader, l2Source) attrBuilder := NewFetchingAttributesBuilder(rollupCfg, l1Fetcher, l2Source) - attributesQueue := NewAttributesQueue(log, rollupCfg, attrBuilder, batchQueue) + attributesQueue := NewAttributesQueue(log, rollupCfg, attrBuilder, batchMux) // Reset from ResetEngine then up from L1 Traversal. The stages do not talk to each other during // the ResetEngine, but after the ResetEngine, this is the order in which the stages could talk to each other. // Note: The ResetEngine is the only reset that can fail. - stages := []ResettableStage{l1Traversal, l1Src, altDA, frameQueue, bank, chInReader, batchQueue, attributesQueue} + stages := []ResettableStage{l1Traversal, l1Src, altDA, frameQueue, channelMux, chInReader, batchMux, attributesQueue} return &DerivationPipeline{ log: log, @@ -127,6 +134,10 @@ func (dp *DerivationPipeline) Reset() { dp.engineIsReset = false } +func (dp *DerivationPipeline) DepositsOnlyAttributes(parent eth.BlockID, derivedFrom eth.L1BlockRef) (*AttributesWithParent, error) { + return dp.attrib.DepositsOnlyAttributes(parent, derivedFrom) +} + // Origin is the L1 block of the inner-most stage of the derivation pipeline, // i.e. the L1 chain up to and including this point included and/or produced all the safe L2 blocks. func (dp *DerivationPipeline) Origin() eth.L1BlockRef { diff --git a/op-node/rollup/engine/build_invalid.go b/op-node/rollup/engine/build_invalid.go index e684f2de2ead..4e58b50b105d 100644 --- a/op-node/rollup/engine/build_invalid.go +++ b/op-node/rollup/engine/build_invalid.go @@ -3,10 +3,9 @@ package engine import ( "fmt" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" ) // BuildInvalidEvent is an internal engine event, to post-process upon invalid attributes. @@ -33,20 +32,19 @@ func (ev InvalidPayloadAttributesEvent) String() string { func (eq *EngDeriver) onBuildInvalid(ev BuildInvalidEvent) { eq.log.Warn("could not process payload attributes", "err", ev.Err) - // Count the number of deposits to see if the tx list is deposit only. - depositCount := 0 - for _, tx := range ev.Attributes.Attributes.Transactions { - if len(tx) > 0 && tx[0] == types.DepositTxType { - depositCount += 1 - } - } // Deposit transaction execution errors are suppressed in the execution engine, but if the // block is somehow invalid, there is nothing we can do to recover & we should exit. - if len(ev.Attributes.Attributes.Transactions) == depositCount { + if ev.Attributes.Attributes.IsDepositsOnly() { eq.log.Error("deposit only block was invalid", "parent", ev.Attributes.Parent, "err", ev.Err) eq.emitter.Emit(rollup.CriticalErrorEvent{Err: fmt.Errorf("failed to process block with only deposit transactions: %w", ev.Err)}) return } + + if ev.Attributes.IsDerived() && eq.cfg.IsHolocene(ev.Attributes.DerivedFrom.Time) { + eq.emitDepositsOnlyPayloadAttributesRequest(ev.Attributes.Parent.ID(), ev.Attributes.DerivedFrom) + return + } + // Revert the pending safe head to the safe head. eq.ec.SetPendingSafeL2Head(eq.ec.SafeL2Head()) // suppress the error b/c we want to retry with the next batch from the batch queue @@ -61,3 +59,12 @@ func (eq *EngDeriver) onBuildInvalid(ev BuildInvalidEvent) { // Signal that we deemed the attributes as unfit eq.emitter.Emit(InvalidPayloadAttributesEvent(ev)) } + +func (eq *EngDeriver) emitDepositsOnlyPayloadAttributesRequest(parent eth.BlockID, derivedFrom eth.L1BlockRef) { + eq.log.Warn("Holocene active, requesting deposits-only attributes", "parent", parent, "derived_from", derivedFrom) + // request deposits-only version + eq.emitter.Emit(derive.DepositsOnlyPayloadAttributesRequestEvent{ + Parent: parent, + DerivedFrom: derivedFrom, + }) +} diff --git a/op-node/rollup/engine/events.go b/op-node/rollup/engine/events.go index 73d0a77abb52..e5de3c86986b 100644 --- a/op-node/rollup/engine/events.go +++ b/op-node/rollup/engine/events.go @@ -25,8 +25,7 @@ type Metrics interface { // forkchoice-update event, to signal the latest forkchoice to other derivers. // This helps decouple derivers from the actual engine state, // while also not making the derivers wait for a forkchoice update at random. -type ForkchoiceRequestEvent struct { -} +type ForkchoiceRequestEvent struct{} func (ev ForkchoiceRequestEvent) String() string { return "forkchoice-request" @@ -184,8 +183,7 @@ func (ev ProcessAttributesEvent) String() string { return "process-attributes" } -type PendingSafeRequestEvent struct { -} +type PendingSafeRequestEvent struct{} func (ev PendingSafeRequestEvent) String() string { return "pending-safe-request" @@ -199,15 +197,13 @@ func (ev ProcessUnsafePayloadEvent) String() string { return "process-unsafe-payload" } -type TryBackupUnsafeReorgEvent struct { -} +type TryBackupUnsafeReorgEvent struct{} func (ev TryBackupUnsafeReorgEvent) String() string { return "try-backup-unsafe-reorg" } -type TryUpdateEngineEvent struct { -} +type TryUpdateEngineEvent struct{} func (ev TryUpdateEngineEvent) String() string { return "try-update-engine" @@ -277,7 +273,8 @@ type EngDeriver struct { var _ event.Deriver = (*EngDeriver)(nil) func NewEngDeriver(log log.Logger, ctx context.Context, cfg *rollup.Config, - metrics Metrics, ec *EngineController) *EngDeriver { + metrics Metrics, ec *EngineController, +) *EngDeriver { return &EngDeriver{ log: log, cfg: cfg, @@ -471,10 +468,10 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { d.onBuildStart(x) case BuildStartedEvent: d.onBuildStarted(x) - case BuildSealedEvent: - d.onBuildSealed(x) case BuildSealEvent: d.onBuildSeal(x) + case BuildSealedEvent: + d.onBuildSealed(x) case BuildInvalidEvent: d.onBuildInvalid(x) case BuildCancelEvent: diff --git a/op-node/rollup/engine/payload_process.go b/op-node/rollup/engine/payload_process.go index 4102287f3d23..68ea1f155d9f 100644 --- a/op-node/rollup/engine/payload_process.go +++ b/op-node/rollup/engine/payload_process.go @@ -30,21 +30,31 @@ func (eq *EngDeriver) onPayloadProcess(ev PayloadProcessEvent) { ev.Envelope.ExecutionPayload, ev.Envelope.ParentBeaconBlockRoot) if err != nil { eq.emitter.Emit(rollup.EngineTemporaryErrorEvent{ - Err: fmt.Errorf("failed to insert execution payload: %w", err)}) + Err: fmt.Errorf("failed to insert execution payload: %w", err), + }) return } switch status.Status { case eth.ExecutionInvalid, eth.ExecutionInvalidBlockHash: + // Depending on execution engine, not all block-validity checks run immediately on build-start + // at the time of the forkchoiceUpdated engine-API call, nor during getPayload. + if ev.DerivedFrom != (eth.L1BlockRef{}) && eq.cfg.IsHolocene(ev.DerivedFrom.Time) { + eq.emitDepositsOnlyPayloadAttributesRequest(ev.Ref.ParentID(), ev.DerivedFrom) + return + } + eq.emitter.Emit(PayloadInvalidEvent{ Envelope: ev.Envelope, - Err: eth.NewPayloadErr(ev.Envelope.ExecutionPayload, status)}) + Err: eth.NewPayloadErr(ev.Envelope.ExecutionPayload, status), + }) return case eth.ExecutionValid: eq.emitter.Emit(PayloadSuccessEvent(ev)) return default: eq.emitter.Emit(rollup.EngineTemporaryErrorEvent{ - Err: eth.NewPayloadErr(ev.Envelope.ExecutionPayload, status)}) + Err: eth.NewPayloadErr(ev.Envelope.ExecutionPayload, status), + }) return } } diff --git a/op-service/eth/types.go b/op-service/eth/types.go index e23a903ebca4..60eb7171d651 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -358,6 +358,31 @@ type PayloadAttributes struct { EIP1559Params *Bytes8 `json:"eip1559Params,omitempty"` } +// IsDepositsOnly returns whether all transactions of the PayloadAttributes are of Deposit +// type. Empty transactions are also considered non-Deposit transactions. +func (a *PayloadAttributes) IsDepositsOnly() bool { + for _, tx := range a.Transactions { + if len(tx) == 0 || tx[0] != types.DepositTxType { + return false + } + } + return true +} + +// WithDepositsOnly return a shallow clone with all non-Deposit transactions stripped from its +// transactions. The order is preserved. +func (a *PayloadAttributes) WithDepositsOnly() *PayloadAttributes { + clone := *a + depositTxs := make([]Data, 0, len(a.Transactions)) + for _, tx := range a.Transactions { + if len(tx) > 0 && tx[0] == types.DepositTxType { + depositTxs = append(depositTxs, tx) + } + } + clone.Transactions = depositTxs + return &clone +} + type ExecutePayloadStatus string const ( From 1d3b29fcb2bc8ffdd368461e5f9d09dc97eb7b4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:36:51 +0700 Subject: [PATCH 086/451] dependabot(gomod): bump github.com/kurtosis-tech/kurtosis/api/golang from 1.3.1 to 1.4.0 (#12748) * dependabot(gomod): bump github.com/kurtosis-tech/kurtosis/api/golang Bumps [github.com/kurtosis-tech/kurtosis/api/golang](https://github.com/kurtosis-tech/kurtosis) from 1.3.1 to 1.4.0. - [Release notes](https://github.com/kurtosis-tech/kurtosis/releases) - [Changelog](https://github.com/kurtosis-tech/kurtosis/blob/main/CHANGELOG.md) - [Commits](https://github.com/kurtosis-tech/kurtosis/compare/1.3.1...1.4.0) --- updated-dependencies: - dependency-name: github.com/kurtosis-tech/kurtosis/api/golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * ci: update kurtosis version --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mark Tyneway --- .circleci/config.yml | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 22edd29aa5ce..7a5d9e3a5ebb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -810,7 +810,7 @@ jobs: command: | echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list sudo apt update - sudo apt install kurtosis-cli=1.3.0 + sudo apt install kurtosis-cli=1.4.0 kurtosis engine start - checkout - when: diff --git a/go.mod b/go.mod index 2d0cf6d664d9..3232ef0d42ce 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-leveldb v0.5.0 github.com/klauspost/compress v1.17.11 - github.com/kurtosis-tech/kurtosis/api/golang v1.3.1 + github.com/kurtosis-tech/kurtosis/api/golang v1.4.0 github.com/libp2p/go-libp2p v0.36.2 github.com/libp2p/go-libp2p-mplex v0.9.0 github.com/libp2p/go-libp2p-pubsub v0.12.0 diff --git a/go.sum b/go.sum index fb1bc5479f4d..b183dc9ae47d 100644 --- a/go.sum +++ b/go.sum @@ -440,8 +440,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230818182330-1a86869414d2 h1:izciXrFyFR+ihJ7nLTOkoIX5GzBPIp8gVKlw94gIc98= github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230818182330-1a86869414d2/go.mod h1:bWSMQK3WHVTGHX9CjxPAb/LtzcmfOxID2wdzakSWQxo= -github.com/kurtosis-tech/kurtosis/api/golang v1.3.1 h1:EBvFnz/mfiIw9a4q3ivBJiapqUiFHanAQh/Cv/jLzV4= -github.com/kurtosis-tech/kurtosis/api/golang v1.3.1/go.mod h1:9T22P7Vv3j5g6sbm78DxHQ4s9C4Cj3s9JjFQ7DFyYpM= +github.com/kurtosis-tech/kurtosis/api/golang v1.4.0 h1:Iczs8diUFpbl4R2JJoofu6R/ZDwBsHe0O6wot45fxgQ= +github.com/kurtosis-tech/kurtosis/api/golang v1.4.0/go.mod h1:9T22P7Vv3j5g6sbm78DxHQ4s9C4Cj3s9JjFQ7DFyYpM= github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20230818184218-f4e3e773463b h1:hMoIM99QKcYQqsnK4AF7Lovi9ZD9ac6lZLZ5D/jx2x8= github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20230818184218-f4e3e773463b/go.mod h1:4pFdrRwDz5R+Fov2ZuTaPhAVgjA2jhGh1Izf832sX7A= github.com/kurtosis-tech/kurtosis/grpc-file-transfer/golang v0.0.0-20230803130419-099ee7a4e3dc h1:7IlEpSehmWcNXOFpNP24Cu5HQI3af7GCBQw//m+LnvQ= From 72c11daa5dbf9ff18c00c138ac5b4082cdc8cffd Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Thu, 31 Oct 2024 16:53:58 +0100 Subject: [PATCH 087/451] op-node/rollup: Promote all attributes to safe post-Holocene (#12724) Also renames fields `IsLastInSpan` to `Safe` because that's semantically clearer. That a last batch in a span batch is pre-Holocene the indicator for a safe block promotion is an implementation detail. It's better to name the effect rather than the reason. --- op-e2e/actions/upgrades/holocene_fork_test.go | 3 +- op-node/rollup/attributes/attributes.go | 2 +- op-node/rollup/attributes/attributes_test.go | 27 +++++---- op-node/rollup/derive/attributes_queue.go | 28 +++++----- op-node/rollup/derive/batch_stage.go | 5 +- op-node/rollup/engine/build_seal.go | 40 +++++++------- op-node/rollup/engine/build_sealed.go | 12 ++-- op-node/rollup/engine/build_start.go | 2 +- op-node/rollup/engine/build_started.go | 6 +- op-node/rollup/engine/events.go | 4 +- op-node/rollup/engine/payload_process.go | 4 +- op-node/rollup/engine/payload_success.go | 8 +-- op-node/rollup/sequencing/sequencer.go | 30 +++++----- .../rollup/sequencing/sequencer_chaos_test.go | 36 ++++++------ op-node/rollup/sequencing/sequencer_test.go | 55 ++++++++++--------- 15 files changed, 131 insertions(+), 131 deletions(-) diff --git a/op-e2e/actions/upgrades/holocene_fork_test.go b/op-e2e/actions/upgrades/holocene_fork_test.go index a51059701d85..7fd46296aaa9 100644 --- a/op-e2e/actions/upgrades/holocene_fork_test.go +++ b/op-e2e/actions/upgrades/holocene_fork_test.go @@ -190,8 +190,7 @@ func TestHoloceneInvalidPayload(gt *testing.T) { env.Seq.ActL1HeadSignal(t) env.Seq.ActL2PipelineFull(t) - // TODO(12695): need to properly update safe after completed L1 block derivation - l2Safe := env.Seq.L2PendingSafe() + l2Safe := env.Seq.L2Safe() require.EqualValues(t, invalidNum, l2Safe.Number) require.NotEqual(t, l2Safe.Hash, l2Unsafe.Hash, // old L2Unsafe above "block-3 should have been replaced by deposit-only version") diff --git a/op-node/rollup/attributes/attributes.go b/op-node/rollup/attributes/attributes.go index 73876c0eaad8..819bc50c24ca 100644 --- a/op-node/rollup/attributes/attributes.go +++ b/op-node/rollup/attributes/attributes.go @@ -194,7 +194,7 @@ func (eq *AttributesHandler) consolidateNextSafeAttributes(attributes *derive.At } eq.emitter.Emit(engine.PromotePendingSafeEvent{ Ref: ref, - Safe: attributes.IsLastInSpan, + Concluding: attributes.Concluding, DerivedFrom: attributes.DerivedFrom, }) } diff --git a/op-node/rollup/attributes/attributes_test.go b/op-node/rollup/attributes/attributes_test.go index c3ed171ce92f..05fbd21fe784 100644 --- a/op-node/rollup/attributes/attributes_test.go +++ b/op-node/rollup/attributes/attributes_test.go @@ -117,9 +117,9 @@ func TestAttributesHandler(t *testing.T) { NoTxPool: false, GasLimit: &payloadA1.ExecutionPayload.GasLimit, }, - Parent: refA0, - IsLastInSpan: true, - DerivedFrom: refB, + Parent: refA0, + Concluding: true, + DerivedFrom: refB, } refA1, err := derive.PayloadToBlockRef(cfg, payloadA1.ExecutionPayload) require.NoError(t, err) @@ -154,9 +154,9 @@ func TestAttributesHandler(t *testing.T) { NoTxPool: false, GasLimit: &payloadA1Alt.ExecutionPayload.GasLimit, }, - Parent: refA0, - IsLastInSpan: true, - DerivedFrom: refBAlt, + Parent: refA0, + Concluding: true, + DerivedFrom: refBAlt, } refA1Alt, err := derive.PayloadToBlockRef(cfg, payloadA1Alt.ExecutionPayload) @@ -272,7 +272,7 @@ func TestAttributesHandler(t *testing.T) { require.Nil(t, ah.attributes, "drop when attributes are successful") }) t.Run("consolidation passes", func(t *testing.T) { - fn := func(t *testing.T, lastInSpan bool) { + fn := func(t *testing.T, concluding bool) { logger := testlog.Logger(t, log.LevelInfo) l2 := &testutils.MockL2Client{} emitter := &testutils.MockEmitter{} @@ -280,10 +280,10 @@ func TestAttributesHandler(t *testing.T) { ah.AttachEmitter(emitter) attr := &derive.AttributesWithParent{ - Attributes: attrA1.Attributes, // attributes will match, passing consolidation - Parent: attrA1.Parent, - IsLastInSpan: lastInSpan, - DerivedFrom: refB, + Attributes: attrA1.Attributes, // attributes will match, passing consolidation + Parent: attrA1.Parent, + Concluding: concluding, + DerivedFrom: refB, } emitter.ExpectOnce(derive.ConfirmReceivedAttributesEvent{}) emitter.ExpectOnce(engine.PendingSafeRequestEvent{}) @@ -296,7 +296,7 @@ func TestAttributesHandler(t *testing.T) { emitter.ExpectOnce(engine.PromotePendingSafeEvent{ Ref: refA1, - Safe: lastInSpan, // last in span becomes safe instantaneously + Concluding: concluding, DerivedFrom: refB, }) ah.OnEvent(engine.PendingSafeUpdateEvent{ @@ -340,7 +340,7 @@ func TestAttributesHandler(t *testing.T) { require.NotNil(t, ah.attributes, "queued up derived attributes") // sanity check test setup - require.True(t, attrA1Alt.IsLastInSpan, "must be last in span for attributes to become safe") + require.True(t, attrA1Alt.Concluding, "must be concluding attributes") // attrA1Alt will fit right on top of A0 emitter.ExpectOnce(engine.BuildStartEvent{Attributes: attrA1Alt}) @@ -396,5 +396,4 @@ func TestAttributesHandler(t *testing.T) { l2.AssertExpectations(t) emitter.AssertExpectations(t) }) - } diff --git a/op-node/rollup/derive/attributes_queue.go b/op-node/rollup/derive/attributes_queue.go index 6823de4ed67d..dba39ef742ff 100644 --- a/op-node/rollup/derive/attributes_queue.go +++ b/op-node/rollup/derive/attributes_queue.go @@ -29,9 +29,9 @@ type AttributesBuilder interface { } type AttributesWithParent struct { - Attributes *eth.PayloadAttributes - Parent eth.L2BlockRef - IsLastInSpan bool + Attributes *eth.PayloadAttributes + Parent eth.L2BlockRef + Concluding bool // Concluding indicates that the attributes conclude the pending safe phase DerivedFrom eth.L1BlockRef } @@ -54,9 +54,9 @@ type AttributesQueue struct { builder AttributesBuilder prev SingularBatchProvider - batch *SingularBatch - isLastInSpan bool - lastAttribs *AttributesWithParent + batch *SingularBatch + concluding bool + lastAttribs *AttributesWithParent } type SingularBatchProvider interface { @@ -82,12 +82,12 @@ func (aq *AttributesQueue) Origin() eth.L1BlockRef { func (aq *AttributesQueue) NextAttributes(ctx context.Context, parent eth.L2BlockRef) (*AttributesWithParent, error) { // Get a batch if we need it if aq.batch == nil { - batch, isLastInSpan, err := aq.prev.NextBatch(ctx, parent) + batch, concluding, err := aq.prev.NextBatch(ctx, parent) if err != nil { return nil, err } aq.batch = batch - aq.isLastInSpan = isLastInSpan + aq.concluding = concluding } // Actually generate the next attributes @@ -96,14 +96,14 @@ func (aq *AttributesQueue) NextAttributes(ctx context.Context, parent eth.L2Bloc } else { // Clear out the local state once we will succeed attr := AttributesWithParent{ - Attributes: attrs, - Parent: parent, - IsLastInSpan: aq.isLastInSpan, - DerivedFrom: aq.Origin(), + Attributes: attrs, + Parent: parent, + Concluding: aq.concluding, + DerivedFrom: aq.Origin(), } aq.lastAttribs = &attr aq.batch = nil - aq.isLastInSpan = false + aq.concluding = false return &attr, nil } } @@ -138,7 +138,7 @@ func (aq *AttributesQueue) createNextAttributes(ctx context.Context, batch *Sing func (aq *AttributesQueue) reset() { aq.batch = nil - aq.isLastInSpan = false // overwritten later, but set for consistency + aq.concluding = false // overwritten later, but set for consistency aq.lastAttribs = nil } diff --git a/op-node/rollup/derive/batch_stage.go b/op-node/rollup/derive/batch_stage.go index 4e4b171b4c9e..d18efed2278e 100644 --- a/op-node/rollup/derive/batch_stage.go +++ b/op-node/rollup/derive/batch_stage.go @@ -70,7 +70,7 @@ func (bs *BatchStage) NextBatch(ctx context.Context, parent eth.L2BlockRef) (*Si // We only consider empty batch generation after we've drained all batches from the local // span batch queue and the previous stage. empty, err := bs.deriveNextEmptyBatch(ctx, true, parent) - // An empty batch always advances the safe head. + // An empty batch always advances the (local) safe head. return empty, true, err } else if err != nil { return nil, false, err @@ -81,7 +81,8 @@ func (bs *BatchStage) NextBatch(ctx context.Context, parent eth.L2BlockRef) (*Si switch validity { case BatchAccept: // continue batch.LogContext(bs.Log()).Debug("Found next singular batch") - return batch, len(bs.nextSpan) == 0, nil + // BatchStage is only used with Holocene, where blocks immediately become (local) safe + return batch, true, nil case BatchPast: batch.LogContext(bs.Log()).Warn("Dropping past singular batch") // NotEnoughData to read in next batch until we're through all past batches diff --git a/op-node/rollup/engine/build_seal.go b/op-node/rollup/engine/build_seal.go index a5c72c74fdb7..8c4e5baeeec7 100644 --- a/op-node/rollup/engine/build_seal.go +++ b/op-node/rollup/engine/build_seal.go @@ -14,8 +14,8 @@ type PayloadSealInvalidEvent struct { Info eth.PayloadInfo Err error - IsLastInSpan bool - DerivedFrom eth.L1BlockRef + Concluding bool + DerivedFrom eth.L1BlockRef } func (ev PayloadSealInvalidEvent) String() string { @@ -30,8 +30,8 @@ type PayloadSealExpiredErrorEvent struct { Info eth.PayloadInfo Err error - IsLastInSpan bool - DerivedFrom eth.L1BlockRef + Concluding bool + DerivedFrom eth.L1BlockRef } func (ev PayloadSealExpiredErrorEvent) String() string { @@ -42,7 +42,7 @@ type BuildSealEvent struct { Info eth.PayloadInfo BuildStarted time.Time // if payload should be promoted to safe (must also be pending safe, see DerivedFrom) - IsLastInSpan bool + Concluding bool // payload is promoted to pending-safe if non-zero DerivedFrom eth.L1BlockRef } @@ -69,10 +69,10 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { // same attributes with a new block-building job from here to recover from this error. // We name it "expired", as this generally identifies a timeout, unknown job, or otherwise invalidated work. eq.emitter.Emit(PayloadSealExpiredErrorEvent{ - Info: ev.Info, - Err: fmt.Errorf("failed to seal execution payload (ID: %s): %w", ev.Info.ID, err), - IsLastInSpan: ev.IsLastInSpan, - DerivedFrom: ev.DerivedFrom, + Info: ev.Info, + Err: fmt.Errorf("failed to seal execution payload (ID: %s): %w", ev.Info.ID, err), + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, }) return } @@ -82,8 +82,8 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { Info: ev.Info, Err: fmt.Errorf("failed sanity-check of execution payload contents (ID: %s, blockhash: %s): %w", ev.Info.ID, envelope.ExecutionPayload.BlockHash, err), - IsLastInSpan: ev.IsLastInSpan, - DerivedFrom: ev.DerivedFrom, + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, }) return } @@ -91,10 +91,10 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { ref, err := derive.PayloadToBlockRef(eq.cfg, envelope.ExecutionPayload) if err != nil { eq.emitter.Emit(PayloadSealInvalidEvent{ - Info: ev.Info, - Err: fmt.Errorf("failed to decode L2 block ref from payload: %w", err), - IsLastInSpan: ev.IsLastInSpan, - DerivedFrom: ev.DerivedFrom, + Info: ev.Info, + Err: fmt.Errorf("failed to decode L2 block ref from payload: %w", err), + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, }) return } @@ -112,10 +112,10 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { "txs", txnCount, "time", ref.Time, "seal_time", sealTime, "build_time", buildTime) eq.emitter.Emit(BuildSealedEvent{ - IsLastInSpan: ev.IsLastInSpan, - DerivedFrom: ev.DerivedFrom, - Info: ev.Info, - Envelope: envelope, - Ref: ref, + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, + Info: ev.Info, + Envelope: envelope, + Ref: ref, }) } diff --git a/op-node/rollup/engine/build_sealed.go b/op-node/rollup/engine/build_sealed.go index d588d77b7f22..eb2680850a75 100644 --- a/op-node/rollup/engine/build_sealed.go +++ b/op-node/rollup/engine/build_sealed.go @@ -7,8 +7,8 @@ import ( // BuildSealedEvent is emitted by the engine when a payload finished building, // but is not locally inserted as canonical block yet type BuildSealedEvent struct { - // if payload should be promoted to safe (must also be pending safe, see DerivedFrom) - IsLastInSpan bool + // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) + Concluding bool // payload is promoted to pending-safe if non-zero DerivedFrom eth.L1BlockRef @@ -25,10 +25,10 @@ func (eq *EngDeriver) onBuildSealed(ev BuildSealedEvent) { // If a (pending) safe block, immediately process the block if ev.DerivedFrom != (eth.L1BlockRef{}) { eq.emitter.Emit(PayloadProcessEvent{ - IsLastInSpan: ev.IsLastInSpan, - DerivedFrom: ev.DerivedFrom, - Envelope: ev.Envelope, - Ref: ev.Ref, + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, + Envelope: ev.Envelope, + Ref: ev.Ref, }) } } diff --git a/op-node/rollup/engine/build_start.go b/op-node/rollup/engine/build_start.go index 22c178d36cd0..2c36053e1bbc 100644 --- a/op-node/rollup/engine/build_start.go +++ b/op-node/rollup/engine/build_start.go @@ -68,7 +68,7 @@ func (eq *EngDeriver) onBuildStart(ev BuildStartEvent) { eq.emitter.Emit(BuildStartedEvent{ Info: eth.PayloadInfo{ID: id, Timestamp: uint64(ev.Attributes.Attributes.Timestamp)}, BuildStarted: buildStartTime, - IsLastInSpan: ev.Attributes.IsLastInSpan, + Concluding: ev.Attributes.Concluding, DerivedFrom: ev.Attributes.DerivedFrom, Parent: ev.Attributes.Parent, }) diff --git a/op-node/rollup/engine/build_started.go b/op-node/rollup/engine/build_started.go index 78b737d214c7..1193c09fb351 100644 --- a/op-node/rollup/engine/build_started.go +++ b/op-node/rollup/engine/build_started.go @@ -13,8 +13,8 @@ type BuildStartedEvent struct { Parent eth.L2BlockRef - // if payload should be promoted to safe (must also be pending safe, see DerivedFrom) - IsLastInSpan bool + // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) + Concluding bool // payload is promoted to pending-safe if non-zero DerivedFrom eth.L1BlockRef } @@ -29,7 +29,7 @@ func (eq *EngDeriver) onBuildStarted(ev BuildStartedEvent) { eq.emitter.Emit(BuildSealEvent{ Info: ev.Info, BuildStarted: ev.BuildStarted, - IsLastInSpan: ev.IsLastInSpan, + Concluding: ev.Concluding, DerivedFrom: ev.DerivedFrom, }) } diff --git a/op-node/rollup/engine/events.go b/op-node/rollup/engine/events.go index e5de3c86986b..bb1dcb66200d 100644 --- a/op-node/rollup/engine/events.go +++ b/op-node/rollup/engine/events.go @@ -109,7 +109,7 @@ func (ev InteropPendingSafeChangedEvent) String() string { // PromotePendingSafeEvent signals that a block can be marked as pending-safe, and/or safe. type PromotePendingSafeEvent struct { Ref eth.L2BlockRef - Safe bool + Concluding bool // Concludes the pending phase, so can be promoted to (local) safe DerivedFrom eth.L1BlockRef } @@ -407,7 +407,7 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { Unsafe: d.ec.UnsafeL2Head(), }) } - if x.Safe && x.Ref.Number > d.ec.LocalSafeL2Head().Number { + if x.Concluding && x.Ref.Number > d.ec.LocalSafeL2Head().Number { d.emitter.Emit(PromoteLocalSafeEvent{ Ref: x.Ref, DerivedFrom: x.DerivedFrom, diff --git a/op-node/rollup/engine/payload_process.go b/op-node/rollup/engine/payload_process.go index 68ea1f155d9f..62d7ded47f0d 100644 --- a/op-node/rollup/engine/payload_process.go +++ b/op-node/rollup/engine/payload_process.go @@ -9,8 +9,8 @@ import ( ) type PayloadProcessEvent struct { - // if payload should be promoted to safe (must also be pending safe, see DerivedFrom) - IsLastInSpan bool + // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) + Concluding bool // payload is promoted to pending-safe if non-zero DerivedFrom eth.L1BlockRef diff --git a/op-node/rollup/engine/payload_success.go b/op-node/rollup/engine/payload_success.go index 7bb4a38307e1..c00d8e81ea77 100644 --- a/op-node/rollup/engine/payload_success.go +++ b/op-node/rollup/engine/payload_success.go @@ -5,8 +5,8 @@ import ( ) type PayloadSuccessEvent struct { - // if payload should be promoted to safe (must also be pending safe, see DerivedFrom) - IsLastInSpan bool + // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) + Concluding bool // payload is promoted to pending-safe if non-zero DerivedFrom eth.L1BlockRef @@ -25,7 +25,7 @@ func (eq *EngDeriver) onPayloadSuccess(ev PayloadSuccessEvent) { if ev.DerivedFrom != (eth.L1BlockRef{}) { eq.emitter.Emit(PromotePendingSafeEvent{ Ref: ev.Ref, - Safe: ev.IsLastInSpan, + Concluding: ev.Concluding, DerivedFrom: ev.DerivedFrom, }) } @@ -34,7 +34,7 @@ func (eq *EngDeriver) onPayloadSuccess(ev PayloadSuccessEvent) { eq.log.Info("Inserted block", "hash", payload.BlockHash, "number", uint64(payload.BlockNumber), "state_root", payload.StateRoot, "timestamp", uint64(payload.Timestamp), "parent", payload.ParentHash, "prev_randao", payload.PrevRandao, "fee_recipient", payload.FeeRecipient, - "txs", len(payload.Transactions), "last_in_span", ev.IsLastInSpan, "derived_from", ev.DerivedFrom) + "txs", len(payload.Transactions), "concluding", ev.Concluding, "derived_from", ev.DerivedFrom) eq.emitter.Emit(TryUpdateEngineEvent{}) } diff --git a/op-node/rollup/sequencing/sequencer.go b/op-node/rollup/sequencing/sequencer.go index 79b65734e5e8..b6605d601fa7 100644 --- a/op-node/rollup/sequencing/sequencer.go +++ b/op-node/rollup/sequencing/sequencer.go @@ -281,10 +281,10 @@ func (d *Sequencer) onBuildSealed(x engine.BuildSealedEvent) { d.asyncGossip.Gossip(x.Envelope) // Now after having gossiped the block, try to put it in our own canonical chain d.emitter.Emit(engine.PayloadProcessEvent{ - IsLastInSpan: x.IsLastInSpan, - DerivedFrom: x.DerivedFrom, - Envelope: x.Envelope, - Ref: x.Ref, + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, + Envelope: x.Envelope, + Ref: x.Ref, }) d.latest.Ref = x.Ref d.latestSealed = x.Ref @@ -334,7 +334,7 @@ func (d *Sequencer) onPayloadSuccess(x engine.PayloadSuccessEvent) { d.asyncGossip.Clear() } -func (d *Sequencer) onSequencerAction(x SequencerActionEvent) { +func (d *Sequencer) onSequencerAction(SequencerActionEvent) { d.log.Debug("Sequencer action") payload := d.asyncGossip.Get() if payload != nil { @@ -356,10 +356,10 @@ func (d *Sequencer) onSequencerAction(x SequencerActionEvent) { // meaning that we have seen BuildSealedEvent already. // We can retry processing to make it canonical. d.emitter.Emit(engine.PayloadProcessEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Envelope: payload, - Ref: ref, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Envelope: payload, + Ref: ref, }) d.latest.Ref = ref } else { @@ -371,7 +371,7 @@ func (d *Sequencer) onSequencerAction(x SequencerActionEvent) { d.emitter.Emit(engine.BuildSealEvent{ Info: d.latest.Info, BuildStarted: d.latest.Started, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) } else if d.latest == (BuildingState{}) { @@ -416,7 +416,7 @@ func (d *Sequencer) onReset(x rollup.ResetEvent) { d.nextActionOK = false } -func (d *Sequencer) onEngineResetConfirmedEvent(x engine.EngineResetConfirmedEvent) { +func (d *Sequencer) onEngineResetConfirmedEvent(engine.EngineResetConfirmedEvent) { d.nextActionOK = d.active.Load() // Before sequencing we can wait a block, // assuming the execution-engine just churned through some work for the reset. @@ -552,10 +552,10 @@ func (d *Sequencer) startBuildingBlock() { // Start a payload building process. withParent := &derive.AttributesWithParent{ - Attributes: attrs, - Parent: l2Head, - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, // zero, not going to be pending-safe / safe + Attributes: attrs, + Parent: l2Head, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, // zero, not going to be pending-safe / safe } // Don't try to start building a block again, until we have heard back from this attempt diff --git a/op-node/rollup/sequencing/sequencer_chaos_test.go b/op-node/rollup/sequencing/sequencer_chaos_test.go index 5d64ab101d7c..d5000fbed339 100644 --- a/op-node/rollup/sequencing/sequencer_chaos_test.go +++ b/op-node/rollup/sequencing/sequencer_chaos_test.go @@ -91,7 +91,7 @@ func (c *ChaoticEngine) OnEvent(ev event.Event) bool { Info: c.currentPayloadInfo, BuildStarted: c.clock.Now(), Parent: x.Attributes.Parent, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) } @@ -124,10 +124,10 @@ func (c *ChaoticEngine) OnEvent(ev event.Event) bool { if c.currentPayloadInfo == (eth.PayloadInfo{}) { c.emitter.Emit(engine.PayloadSealExpiredErrorEvent{ - Info: x.Info, - Err: errors.New("job was cancelled"), - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, + Info: x.Info, + Err: errors.New("job was cancelled"), + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, }) return true } @@ -142,17 +142,17 @@ func (c *ChaoticEngine) OnEvent(ev event.Event) bool { switch { case p < 0.03: // 3% c.emitter.Emit(engine.PayloadSealInvalidEvent{ - Info: x.Info, - Err: errors.New("mock invalid seal"), - IsLastInSpan: x.IsLastInSpan, - DerivedFrom: x.DerivedFrom, + Info: x.Info, + Err: errors.New("mock invalid seal"), + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, }) case p < 0.08: // 5% c.emitter.Emit(engine.PayloadSealExpiredErrorEvent{ - Info: x.Info, - Err: errors.New("mock temp engine error"), - IsLastInSpan: x.IsLastInSpan, - DerivedFrom: x.DerivedFrom, + Info: x.Info, + Err: errors.New("mock temp engine error"), + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, }) default: payloadEnvelope := ð.ExecutionPayloadEnvelope{ @@ -178,11 +178,11 @@ func (c *ChaoticEngine) OnEvent(ev event.Event) bool { SequenceNumber: 0, // ignored } c.emitter.Emit(engine.BuildSealedEvent{ - Info: x.Info, - Envelope: payloadEnvelope, - Ref: payloadRef, - IsLastInSpan: x.IsLastInSpan, - DerivedFrom: x.DerivedFrom, + Info: x.Info, + Envelope: payloadEnvelope, + Ref: payloadRef, + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, }) } c.currentPayloadInfo = eth.PayloadInfo{} diff --git a/op-node/rollup/sequencing/sequencer_test.go b/op-node/rollup/sequencing/sequencer_test.go index 18fee8f5a186..9c4e77ee7253 100644 --- a/op-node/rollup/sequencing/sequencer_test.go +++ b/op-node/rollup/sequencing/sequencer_test.go @@ -46,7 +46,8 @@ func decodeID(data []byte) eth.BlockID { } func (m *FakeAttributesBuilder) PreparePayloadAttributes(ctx context.Context, - l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) { + l2Parent eth.L2BlockRef, epoch eth.BlockID, +) (attrs *eth.PayloadAttributes, err error) { gasLimit := eth.Uint64Quantity(30_000_000) attrs = ð.PayloadAttributes{ Timestamp: eth.Uint64Quantity(l2Parent.Time + m.cfg.BlockTime), @@ -315,7 +316,7 @@ func TestSequencer_StaleBuild(t *testing.T) { Info: payloadInfo, BuildStarted: startedTime, Parent: head, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) @@ -325,7 +326,7 @@ func TestSequencer_StaleBuild(t *testing.T) { emitter.ExpectOnce(engine.BuildSealEvent{ Info: payloadInfo, BuildStarted: startedTime, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) seq.OnEvent(SequencerActionEvent{}) @@ -355,18 +356,18 @@ func TestSequencer_StaleBuild(t *testing.T) { SequenceNumber: 0, } emitter.ExpectOnce(engine.PayloadProcessEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Envelope: payloadEnvelope, - Ref: payloadRef, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Envelope: payloadEnvelope, + Ref: payloadRef, }) // And report back the sealing result to the engine seq.OnEvent(engine.BuildSealedEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Info: payloadInfo, - Envelope: payloadEnvelope, - Ref: payloadRef, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Info: payloadInfo, + Envelope: payloadEnvelope, + Ref: payloadRef, }) // The sequencer should start processing the payload emitter.AssertExpectations(t) @@ -521,7 +522,7 @@ func TestSequencerBuild(t *testing.T) { Info: payloadInfo, BuildStarted: startedTime, Parent: head, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) // The sealing should now be scheduled as next action. @@ -535,7 +536,7 @@ func TestSequencerBuild(t *testing.T) { emitter.ExpectOnce(engine.BuildSealEvent{ Info: payloadInfo, BuildStarted: startedTime, - IsLastInSpan: false, + Concluding: false, DerivedFrom: eth.L1BlockRef{}, }) seq.OnEvent(SequencerActionEvent{}) @@ -564,18 +565,18 @@ func TestSequencerBuild(t *testing.T) { SequenceNumber: 0, } emitter.ExpectOnce(engine.PayloadProcessEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Envelope: payloadEnvelope, - Ref: payloadRef, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Envelope: payloadEnvelope, + Ref: payloadRef, }) // And report back the sealing result to the engine seq.OnEvent(engine.BuildSealedEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Info: payloadInfo, - Envelope: payloadEnvelope, - Ref: payloadRef, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Info: payloadInfo, + Envelope: payloadEnvelope, + Ref: payloadRef, }) // The sequencer should start processing the payload emitter.AssertExpectations(t) @@ -587,10 +588,10 @@ func TestSequencerBuild(t *testing.T) { // Mock that the processing was successful seq.OnEvent(engine.PayloadSuccessEvent{ - IsLastInSpan: false, - DerivedFrom: eth.L1BlockRef{}, - Envelope: payloadEnvelope, - Ref: payloadRef, + Concluding: false, + DerivedFrom: eth.L1BlockRef{}, + Envelope: payloadEnvelope, + Ref: payloadRef, }) require.Nil(t, deps.asyncGossip.payload, "async gossip should have cleared,"+ " after previous publishing and now having persisted the block ourselves") From caf63ce1dd8afbc66c9d042926f788775db6b01e Mon Sep 17 00:00:00 2001 From: Sam Stokes <35908605+bitwiseguy@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:19:18 -0400 Subject: [PATCH 088/451] op-deployer: add 'inspect superchain-registry' subcommand (#12736) * op-deployer: add 'inspect superchain-registry' subcommand * op-deployer: generate artifacts at runtime of superchain-registry cmd * op-deployer: add missing err handler * op-deployer: add Printf to prompt user to populate .env vals * op-deployer: reformat addresses to superchain.AddressList --- .../pkg/deployer/inspect/deploy_config.go | 35 ++-- op-deployer/pkg/deployer/inspect/flags.go | 8 + op-deployer/pkg/deployer/inspect/l1.go | 36 ++-- .../deployer/inspect/superchain_registry.go | 173 ++++++++++++++++++ op-deployer/pkg/deployer/standard/standard.go | 22 +++ 5 files changed, 250 insertions(+), 24 deletions(-) create mode 100644 op-deployer/pkg/deployer/inspect/superchain_registry.go diff --git a/op-deployer/pkg/deployer/inspect/deploy_config.go b/op-deployer/pkg/deployer/inspect/deploy_config.go index ead006c7d622..575576633ab7 100644 --- a/op-deployer/pkg/deployer/inspect/deploy_config.go +++ b/op-deployer/pkg/deployer/inspect/deploy_config.go @@ -3,10 +3,12 @@ package inspect import ( "fmt" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil" + "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" ) @@ -18,29 +20,40 @@ func DeployConfigCLI(cliCtx *cli.Context) error { globalState, err := pipeline.ReadState(cliCfg.Workdir) if err != nil { - return fmt.Errorf("failed to read intent: %w", err) + return fmt.Errorf("failed to read globalState: %w", err) } - chainState, err := globalState.Chain(cliCfg.ChainID) + + config, err := DeployConfig(globalState, cliCfg.ChainID) if err != nil { - return fmt.Errorf("failed to find chain state: %w", err) + return fmt.Errorf("failed to generate deploy config: %w", err) + } + + if err := jsonutil.WriteJSON(config, ioutil.ToStdOutOrFileOrNoop(cliCfg.Outfile, 0o666)); err != nil { + return fmt.Errorf("failed to write deploy config: %w", err) + } + + return nil +} + +func DeployConfig(globalState *state.State, chainID common.Hash) (*genesis.DeployConfig, error) { + chainState, err := globalState.Chain(chainID) + if err != nil { + return nil, fmt.Errorf("failed to find chain state: %w", err) } intent := globalState.AppliedIntent if intent == nil { - return fmt.Errorf("can only run this command following a full apply") + return nil, fmt.Errorf("can only run this command following a full apply") } - chainIntent, err := intent.Chain(cliCfg.ChainID) + chainIntent, err := intent.Chain(chainID) if err != nil { - return fmt.Errorf("failed to find chain intent: %w", err) + return nil, fmt.Errorf("failed to find chain intent: %w", err) } config, err := state.CombineDeployConfig(intent, chainIntent, globalState, chainState) if err != nil { - return fmt.Errorf("failed to generate deploy config: %w", err) - } - if err := jsonutil.WriteJSON(config, ioutil.ToStdOutOrFileOrNoop(cliCfg.Outfile, 0o666)); err != nil { - return fmt.Errorf("failed to write deploy config: %w", err) + return nil, fmt.Errorf("failed to generate deploy config: %w", err) } - return nil + return &config, nil } diff --git a/op-deployer/pkg/deployer/inspect/flags.go b/op-deployer/pkg/deployer/inspect/flags.go index 2f94edc088e5..3f74e1d759b2 100644 --- a/op-deployer/pkg/deployer/inspect/flags.go +++ b/op-deployer/pkg/deployer/inspect/flags.go @@ -69,6 +69,14 @@ var Commands = []*cli.Command{ Action: L2SemversCLI, Flags: Flags, }, + { + Name: "superchain-registry", + Usage: "outputs the .env file expected by superchain-registry add-chain tool", + Args: true, + ArgsUsage: "", + Action: SuperchainRegistryCLI, + Flags: Flags, + }, } type cliConfig struct { diff --git a/op-deployer/pkg/deployer/inspect/l1.go b/op-deployer/pkg/deployer/inspect/l1.go index a5391810aec5..d0a4f88172d1 100644 --- a/op-deployer/pkg/deployer/inspect/l1.go +++ b/op-deployer/pkg/deployer/inspect/l1.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil" @@ -37,9 +38,9 @@ type OpChainDeployment struct { DisputeGameFactoryProxyAddress common.Address `json:"disputeGameFactoryProxyAddress"` AnchorStateRegistryProxyAddress common.Address `json:"anchorStateRegistryProxyAddress"` AnchorStateRegistryImplAddress common.Address `json:"anchorStateRegistryImplAddress"` - // FaultDisputeGameAddress common.Address `json:"faultDisputeGameAddress"` - PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"` - DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` + FaultDisputeGameAddress common.Address `json:"faultDisputeGameAddress"` + PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"` + DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` // DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"` } @@ -68,9 +69,22 @@ func L1CLI(cliCtx *cli.Context) error { return fmt.Errorf("failed to read intent: %w", err) } - chainState, err := globalState.Chain(cfg.ChainID) + l1Contracts, err := L1(globalState, cfg.ChainID) if err != nil { - return fmt.Errorf("failed to get chain state for ID %s: %w", cfg.ChainID.String(), err) + return fmt.Errorf("failed to generate l1Contracts: %w", err) + } + + if err := jsonutil.WriteJSON(l1Contracts, ioutil.ToStdOutOrFileOrNoop(cfg.Outfile, 0o666)); err != nil { + return fmt.Errorf("failed to write L1 contract addresses: %w", err) + } + + return nil +} + +func L1(globalState *state.State, chainID common.Hash) (*L1Contracts, error) { + chainState, err := globalState.Chain(chainID) + if err != nil { + return nil, fmt.Errorf("failed to get chain state for ID %s: %w", chainID.String(), err) } l1Contracts := L1Contracts{ @@ -93,9 +107,9 @@ func L1CLI(cliCtx *cli.Context) error { DisputeGameFactoryProxyAddress: chainState.DisputeGameFactoryProxyAddress, AnchorStateRegistryProxyAddress: chainState.AnchorStateRegistryProxyAddress, AnchorStateRegistryImplAddress: chainState.AnchorStateRegistryImplAddress, - // FaultDisputeGameAddress: chainState.FaultDisputeGameAddress, - PermissionedDisputeGameAddress: chainState.PermissionedDisputeGameAddress, - DelayedWETHPermissionedGameProxyAddress: chainState.DelayedWETHPermissionedGameProxyAddress, + FaultDisputeGameAddress: chainState.FaultDisputeGameAddress, + PermissionedDisputeGameAddress: chainState.PermissionedDisputeGameAddress, + DelayedWETHPermissionedGameProxyAddress: chainState.DelayedWETHPermissionedGameProxyAddress, // DelayedWETHPermissionlessGameProxyAddress: chainState.DelayedWETHPermissionlessGameProxyAddress, }, ImplementationsDeployment: ImplementationsDeployment{ @@ -113,9 +127,5 @@ func L1CLI(cliCtx *cli.Context) error { }, } - if err := jsonutil.WriteJSON(l1Contracts, ioutil.ToStdOutOrFileOrNoop(cfg.Outfile, 0o666)); err != nil { - return fmt.Errorf("failed to write L1 contract addresses: %w", err) - } - - return nil + return &l1Contracts, nil } diff --git a/op-deployer/pkg/deployer/inspect/superchain_registry.go b/op-deployer/pkg/deployer/inspect/superchain_registry.go new file mode 100644 index 000000000000..7a6fe384db64 --- /dev/null +++ b/op-deployer/pkg/deployer/inspect/superchain_registry.go @@ -0,0 +1,173 @@ +package inspect + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/superchain-registry/superchain" + + "github.com/urfave/cli/v2" +) + +func SuperchainRegistryCLI(cliCtx *cli.Context) error { + cfg, err := readConfig(cliCtx) + if err != nil { + return err + } + + globalIntent, err := pipeline.ReadIntent(cfg.Workdir) + if err != nil { + return fmt.Errorf("failed to read intent: %w", err) + } + + envVars := map[string]string{} + envVars["SCR_CHAIN_NAME"] = "" + envVars["SCR_CHAIN_SHORT_NAME"] = "" + envVars["SCR_PUBLIC_RPC"] = "" + envVars["SCR_SEQUENCER_RPC"] = "" + envVars["SCR_EXPLORER"] = "" + envVars["SCR_STANDARD_CHAIN_CANDIDATE"] = "false" + + creationCommit, err := standard.CommitForDeployTag(globalIntent.L2ContractsLocator.Tag) + if err != nil { + return fmt.Errorf("failed to get commit for deploy tag: %w", err) + } + envVars["SCR_GENESIS_CREATION_COMMIT"] = creationCommit + + l1ChainName, err := standard.ChainNameFor(globalIntent.L1ChainID) + if err != nil { + return fmt.Errorf("failed to get l1 chain name: %w", err) + } + envVars["SCR_SUPERCHAIN_TARGET"] = l1ChainName + + globalState, err := pipeline.ReadState(cfg.Workdir) + if err != nil { + return fmt.Errorf("failed to read state: %w", err) + } + + genesis, rollup, err := GenesisAndRollup(globalState, cfg.ChainID) + if err != nil { + return fmt.Errorf("failed to generate genesis and rollup: %w", err) + } + genesisFilepath := filepath.Join(cfg.Workdir, "genesis.json") + if err := jsonutil.WriteJSON(genesis, ioutil.ToStdOutOrFileOrNoop(genesisFilepath, 0o666)); err != nil { + return fmt.Errorf("failed to write genesis: %w", err) + } + rollupFilepath := filepath.Join(cfg.Workdir, "rollup.json") + if err := jsonutil.WriteJSON(rollup, ioutil.ToStdOutOrFileOrNoop(rollupFilepath, 0o666)); err != nil { + return fmt.Errorf("failed to write rollup: %w", err) + } + + deployConfig, err := DeployConfig(globalState, cfg.ChainID) + if err != nil { + return fmt.Errorf("failed to generate deploy config: %w", err) + } + deployConfigFilepath := filepath.Join(cfg.Workdir, "deploy-config.json") + if err := jsonutil.WriteJSON(deployConfig, ioutil.ToStdOutOrFileOrNoop(deployConfigFilepath, 0o666)); err != nil { + return fmt.Errorf("failed to write rollup: %w", err) + } + + l1Contracts, err := L1(globalState, cfg.ChainID) + if err != nil { + return fmt.Errorf("failed to generate l1 contracts: %w", err) + } + + addressList, err := createAddressList(l1Contracts, globalState.AppliedIntent, cfg.ChainID) + if err != nil { + return fmt.Errorf("failed to create address list: %w", err) + } + + addressListFilepath := filepath.Join(cfg.Workdir, "addresses.json") + if err := jsonutil.WriteJSON(addressList, ioutil.ToStdOutOrFileOrNoop(addressListFilepath, 0o666)); err != nil { + return fmt.Errorf("failed to write address list: %w", err) + } + + envVars["SCR_GENESIS"] = genesisFilepath + envVars["SCR_ROLLUP_CONFIG"] = rollupFilepath + envVars["SCR_DEPLOY_CONFIG"] = deployConfigFilepath + envVars["SCR_DEPLOYMENTS_DIR"] = addressListFilepath + + envFilepath := filepath.Join(cfg.Workdir, "superchain-registry.env") + err = writeEnvFile(envFilepath, envVars) + if err != nil { + return fmt.Errorf("failed to write .env file: %w", err) + } + + fmt.Printf("---------------------------------------------------\n"+ + "Please populate any empty values in your .env file\n"+ + "before creating your pull-request to add this chain\n"+ + "to the superchain-registry repo.\n\n"+ + " * %s\n"+ + "---------------------------------------------------\n", envFilepath, + ) + + return nil +} + +func writeEnvFile(filepath string, envVars map[string]string) error { + file, err := os.Create(filepath) + if err != nil { + return err + } + defer file.Close() + + for key, value := range envVars { + _, err := file.WriteString(fmt.Sprintf("%s=\"%s\"\n", key, value)) + if err != nil { + return err + } + } + + return nil +} + +func createAddressList(l1Contracts *L1Contracts, appliedIntent *state.Intent, chainId common.Hash) (*superchain.AddressList, error) { + chainIntent, err := appliedIntent.Chain(chainId) + if err != nil { + return nil, fmt.Errorf("failed to get applied chain intent: %w", err) + } + + addressList := superchain.AddressList{ + // Roles + Roles: superchain.Roles{ + Guardian: superchain.Address(appliedIntent.SuperchainRoles.Guardian), + SystemConfigOwner: superchain.Address(chainIntent.Roles.SystemConfigOwner), + ProxyAdminOwner: superchain.Address(chainIntent.Roles.L1ProxyAdminOwner), + Challenger: superchain.Address(chainIntent.Roles.Challenger), + Proposer: superchain.Address(chainIntent.Roles.Proposer), + UnsafeBlockSigner: superchain.Address(chainIntent.Roles.UnsafeBlockSigner), + BatchSubmitter: superchain.Address(chainIntent.Roles.Batcher), + }, + + // Contracts + AddressManager: superchain.Address(l1Contracts.OpChainDeployment.AddressManagerAddress), + L1CrossDomainMessengerProxy: superchain.Address(l1Contracts.OpChainDeployment.L1CrossDomainMessengerProxyAddress), + L1ERC721BridgeProxy: superchain.Address(l1Contracts.OpChainDeployment.L1ERC721BridgeProxyAddress), + L1StandardBridgeProxy: superchain.Address(l1Contracts.OpChainDeployment.L1StandardBridgeProxyAddress), + OptimismMintableERC20FactoryProxy: superchain.Address(l1Contracts.OpChainDeployment.OptimismMintableERC20FactoryProxyAddress), + OptimismPortalProxy: superchain.Address(l1Contracts.OpChainDeployment.OptimismPortalProxyAddress), + SystemConfigProxy: superchain.Address(l1Contracts.OpChainDeployment.SystemConfigProxyAddress), + + ProxyAdmin: superchain.Address(l1Contracts.OpChainDeployment.ProxyAdminAddress), + SuperchainConfig: superchain.Address(l1Contracts.SuperchainDeployment.SuperchainConfigProxyAddress), + + // Fault proof contracts + AnchorStateRegistryProxy: superchain.Address(l1Contracts.OpChainDeployment.AnchorStateRegistryProxyAddress), + DelayedWETHProxy: superchain.Address(l1Contracts.OpChainDeployment.L1CrossDomainMessengerProxyAddress), + DisputeGameFactoryProxy: superchain.Address(l1Contracts.OpChainDeployment.DisputeGameFactoryProxyAddress), + FaultDisputeGame: superchain.Address(l1Contracts.OpChainDeployment.FaultDisputeGameAddress), + MIPS: superchain.Address(l1Contracts.ImplementationsDeployment.MipsSingletonAddress), + PermissionedDisputeGame: superchain.Address(l1Contracts.OpChainDeployment.PermissionedDisputeGameAddress), + PreimageOracle: superchain.Address(l1Contracts.ImplementationsDeployment.PreimageOracleSingletonAddress), + } + + return &addressList, nil +} diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index ba3031bbe99e..2666891d96fc 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -105,6 +105,28 @@ func SuperchainFor(chainID uint64) (*superchain.Superchain, error) { } } +func ChainNameFor(chainID uint64) (string, error) { + switch chainID { + case 1: + return "mainnet", nil + case 11155111: + return "sepolia", nil + default: + return "", fmt.Errorf("unrecognized chain ID: %d", chainID) + } +} + +func CommitForDeployTag(tag string) (string, error) { + switch tag { + case "op-contracts/v1.6.0": + return "33f06d2d5e4034125df02264a5ffe84571bd0359", nil + case "op-contracts/v1.7.0-beta.1+l2-contracts": + return "5e14a61547a45eef2ebeba677aee4a049f106ed8", nil + default: + return "", fmt.Errorf("unsupported tag: %s", tag) + } +} + func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) { switch chainID { case 1: From 7f941b84c03beedee232eac9b5af2ff6f9a0d820 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 31 Oct 2024 23:21:32 +0700 Subject: [PATCH 089/451] Interop: local devnet (#11590) * local interop devnet * interop-devnet: experimental op-geth docker image, connect with op-supervisor * interop-devnet: port and path fixes * interop-devnet: datadir fix * interop-local: more fixes * interop-devnet: connect op-supervisor to L2 EL nodes using RPC * more fixes * ops-bedrock: fix l2 op geth dockerfile for interop * interop-devnet: fix supervisor RPC add workaround * interop-devnet: implement review suggestions * fixes from run-testing * Add op-deployer to dockerignore exceptions * use latest geth rc * use RW Locks in Update Functions * add log for new cross-safe head * make updates much more frequent * use LocalDB for LastDerivedFrom * Add log message for finalization update * op-supervisor: fix db locking, fix crossdb usage * interop-devnet: use chain IDs as chain indices, since it's not translated everywhere yet * op-supervisor: cross-derived-from RPC method * Work Process ErrFuture to Debug Log --------- Co-authored-by: axelKingsley --- interop-devnet/create-chains.sh | 77 ++++ interop-devnet/depset.json | 14 + interop-devnet/docker-compose.yml | 354 ++++++++++++++++++ interop-devnet/justfile | 33 ++ op-chain-ops/devkeys/devkeys.go | 30 ++ op-node/cmd/interop/interop.go | 299 +++++++++++++++ op-node/cmd/main.go | 2 + op-node/flags/flags.go | 2 +- op-node/rollup/interop/interop.go | 6 +- op-service/ioutil/streams.go | 16 + op-service/jsonutil/json.go | 1 + op-service/locks/rwmap.go | 48 +++ op-service/locks/rwmap_test.go | 52 +++ op-service/locks/rwvalue.go | 24 ++ op-service/locks/rwvalue_test.go | 16 + op-service/sources/supervisor_client.go | 4 +- op-service/testutils/fake_interop_backend.go | 2 +- op-service/testutils/mock_interop_backend.go | 6 +- op-supervisor/supervisor/backend/backend.go | 4 +- .../backend/cross/safe_start_test.go | 8 +- .../supervisor/backend/cross/worker.go | 9 +- op-supervisor/supervisor/backend/db/db.go | 77 ++-- .../backend/db/fromda/update_test.go | 2 +- op-supervisor/supervisor/backend/db/query.go | 116 ++---- op-supervisor/supervisor/backend/db/update.go | 44 +-- op-supervisor/supervisor/backend/mock.go | 2 +- op-supervisor/supervisor/frontend/frontend.go | 6 +- op-supervisor/supervisor/service.go | 11 + ops-bedrock/l2-op-geth-interop.Dockerfile | 10 + .../op-stack-go/Dockerfile.dockerignore | 1 + 30 files changed, 1091 insertions(+), 185 deletions(-) create mode 100755 interop-devnet/create-chains.sh create mode 100644 interop-devnet/depset.json create mode 100644 interop-devnet/docker-compose.yml create mode 100644 interop-devnet/justfile create mode 100644 op-node/cmd/interop/interop.go create mode 100644 op-service/locks/rwmap.go create mode 100644 op-service/locks/rwmap_test.go create mode 100644 op-service/locks/rwvalue.go create mode 100644 op-service/locks/rwvalue_test.go create mode 100644 ops-bedrock/l2-op-geth-interop.Dockerfile diff --git a/interop-devnet/create-chains.sh b/interop-devnet/create-chains.sh new file mode 100755 index 000000000000..506682099154 --- /dev/null +++ b/interop-devnet/create-chains.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +set -eu + +# Run this with workdir set as root of the repo +if [ -f "../versions.json" ]; then + echo "Running create-chains script." +else + echo "Cannot run create-chains script, must be in interop-devnet dir, but currently in:" + pwd + exit 1 +fi + +# Navigate to repository root +cd .. + +# Check if already created +if [ -d ".devnet-interop" ]; then + echo "Already created chains." + exit 1 +else + echo "Creating new interop devnet chain configs" +fi + +export OP_INTEROP_MNEMONIC="test test test test test test test test test test test junk" + +go run ./op-node/cmd interop dev-setup \ + --artifacts-dir=packages/contracts-bedrock/forge-artifacts \ + --foundry-dir=packages/contracts-bedrock \ + --l1.chainid=900100 \ + --l2.chainids=900200,900201 \ + --out-dir=".devnet-interop" \ + --log.format=logfmt \ + --log.level=info + +# create L1 CL genesis +eth2-testnet-genesis deneb \ + --config=./ops-bedrock/beacon-data/config.yaml \ + --preset-phase0=minimal \ + --preset-altair=minimal \ + --preset-bellatrix=minimal \ + --preset-capella=minimal \ + --preset-deneb=minimal \ + --eth1-config=.devnet-interop/genesis/l1/genesis.json \ + --state-output=.devnet-interop/genesis/l1/beaconstate.ssz \ + --tranches-dir=.devnet-interop/genesis/l1/tranches \ + --mnemonics=./ops-bedrock/mnemonics.yaml \ + --eth1-withdrawal-address=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ + --eth1-match-genesis-time + +echo "Writing env files now..." + +# write env files for each L2 service + +chain_env=".devnet-interop/env/l2/900200" +mkdir -p "$chain_env" +key_cmd="go run ./op-node/cmd interop devkey secret --domain=chain-operator --chainid=900200" +# op-node +echo "OP_NODE_P2P_SEQUENCER_KEY=$($key_cmd --name=sequencer-p2p)" >> "$chain_env/op-node.env" +# proposer +echo "OP_PROPOSER_PRIVATE_KEY=$($key_cmd --name=proposer)" >> "$chain_env/op-proposer.env" +echo "OP_PROPOSER_GAME_FACTORY_ADDRESS=$(jq -r .DisputeGameFactoryProxy .devnet-interop/deployments/l2/900200/addresses.json)" >> "$chain_env/op-proposer.env" +# batcher +echo "OP_BATCHER_PRIVATE_KEY=$($key_cmd --name=batcher)" >> "$chain_env/op-batcher.env" + +chain_env=".devnet-interop/env/l2/900201" +mkdir -p "$chain_env" +key_cmd="go run ./op-node/cmd interop devkey secret --domain=chain-operator --chainid=900201" +# op-node +echo "OP_NODE_P2P_SEQUENCER_KEY=$($key_cmd --name=sequencer-p2p)" >> "$chain_env/op-node.env" +# proposer +echo "OP_PROPOSER_PRIVATE_KEY=$($key_cmd --name=proposer)" >> "$chain_env/op-proposer.env" +echo "OP_PROPOSER_GAME_FACTORY_ADDRESS=$(jq -r .DisputeGameFactoryProxy .devnet-interop/deployments/l2/900201/addresses.json)" >> "$chain_env/op-proposer.env" +# batcher +echo "OP_BATCHER_PRIVATE_KEY=$($key_cmd --name=batcher)" >> "$chain_env/op-batcher.env" + +echo "Interop devnet setup is complete!" diff --git a/interop-devnet/depset.json b/interop-devnet/depset.json new file mode 100644 index 000000000000..6f3600b1d296 --- /dev/null +++ b/interop-devnet/depset.json @@ -0,0 +1,14 @@ +{ + "dependencies": { + "900200": { + "chainIndex": "900200", + "activationTime": 0, + "historyMinTime": 0 + }, + "900201": { + "chainIndex": "900201", + "activationTime": 0, + "historyMinTime": 0 + } + } +} diff --git a/interop-devnet/docker-compose.yml b/interop-devnet/docker-compose.yml new file mode 100644 index 000000000000..97ddcbda655f --- /dev/null +++ b/interop-devnet/docker-compose.yml @@ -0,0 +1,354 @@ +# This Compose file is expected to be used with the devnet-up.sh script. +# The volumes below mount the configs generated by the script into each +# service. + +volumes: + l1_data: + l1_bn_data: + l1_vc_data: + l2_a_data: + safedb_a_data: + l2_b_data: + safedb_b_data: + supervisor_data: + op_log_a: + op_log_b: + +services: + + l1: + build: + context: ../ops-bedrock + dockerfile: l1-geth.Dockerfile + ports: + - "8545:8545" + - "8546:8546" + - "7060:6060" + volumes: + - "l1_data:/db" + - "${PWD}/../.devnet-interop/genesis/l1/genesis.json:/genesis.json" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + environment: + GETH_MINER_RECOMMIT: 100ms + + l1-bn: + depends_on: + - l1 + build: + context: ../ops-bedrock + dockerfile: l1-lighthouse.Dockerfile + ports: + - "9000:9000" + - "5052:5052" + volumes: + - "l1_bn_data:/db" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + - "${PWD}/../ops-bedrock/beacon-data/config.yaml:/genesis/config.yaml" + - "${PWD}/../ops-bedrock/beacon-data/deposit_contract_block.txt:/genesis/deposit_contract_block.txt" + - "${PWD}/../.devnet-interop/genesis/l1/beaconstate.ssz:/genesis/genesis.ssz" + environment: + LH_EXECUTION_ENDPOINT: "http://l1:8551" + entrypoint: + - "/bin/sh" + - "/entrypoint-bn.sh" + + l1-vc: + depends_on: + - l1 + - l1-bn + build: + context: ../ops-bedrock + dockerfile: l1-lighthouse.Dockerfile + volumes: + - "l1_vc_data:/db" + - "${PWD}/../ops-bedrock/beacon-data/data/keys:/validator_setup/validators" + - "${PWD}/../ops-bedrock/beacon-data/data/secrets:/validator_setup/secrets" + - "${PWD}/../ops-bedrock/beacon-data/config.yaml:/genesis/config.yaml" + - "${PWD}/../ops-bedrock/beacon-data/deposit_contract_block.txt:/genesis/deposit_contract_block.txt" + - "${PWD}/../.devnet-interop/genesis/l1/beaconstate.ssz:/genesis/genesis.ssz" + environment: + LH_BEACON_NODES: "http://l1-bn:5052/" + entrypoint: + - "/bin/sh" + - "/entrypoint-vc.sh" + + op-supervisor: + depends_on: + - l1 + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-supervisor-target + ports: + - "9045:8545" + volumes: + - "supervisor_data:/db" + - "./depset.json:/depset.json" + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-supervisor:devnet + command: > + op-supervisor + --datadir="/db" + --dependency-set="/depset.json" + --l2-rpcs="" + --rpc.addr="0.0.0.0" + --rpc.port=8545 + --rpc.enable-admin + --l2-rpcs="ws://l2-a:8546,ws://l2-b:8546" + + l2-a: + depends_on: + - op-supervisor + build: + context: ../ops-bedrock/ + dockerfile: l2-op-geth-interop.Dockerfile + ports: + - "9145:8545" + - "8160:6060" + volumes: + - "l2_a_data:/db" + - "${PWD}/../.devnet-interop/genesis/l2/900200/genesis.json:/genesis.json" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + entrypoint: # pass the L2 specific flags by overriding the entry-point and adding extra arguments + - "/bin/sh" + - "/entrypoint.sh" + environment: + GETH_MINER_RECOMMIT: 100ms + GETH_ROLLUP_INTEROPRPC: "ws://op-supervisor:8545" + + l2-b: + depends_on: + - op-supervisor + build: + context: ../ops-bedrock/ + dockerfile: l2-op-geth-interop.Dockerfile + ports: + - "9245:8545" + - "8260:6060" + volumes: + - "l2_b_data:/db" + - "${PWD}/../.devnet-interop/genesis/l2/900201/genesis.json:/genesis.json" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + entrypoint: # pass the L2 specific flags by overriding the entry-point and adding extra arguments + - "/bin/sh" + - "/entrypoint.sh" + environment: + GETH_MINER_RECOMMIT: 100ms + GETH_ROLLUP_INTEROPRPC: "ws://op-supervisor:8545" + + op-node-a: + depends_on: + - l1 + - l1-bn + - l1-vc + - l2-a + - op-supervisor + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-node-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:devnet + command: > + op-node + --l1=ws://l1:8546 + --l1.beacon=http://l1-bn:5052 + --l1.epoch-poll-interval=12s + --l1.http-poll-interval=6s + --l2=http://l2-a:8551 + --l2.jwt-secret=/config/jwt-secret.txt + --supervisor=http://op-supervisor:8545 + --sequencer.enabled + --sequencer.l1-confs=0 + --verifier.l1-confs=0 + --rollup.config=/rollup.json + --rpc.addr=0.0.0.0 + --rpc.port=8545 + --p2p.listen.ip=0.0.0.0 + --p2p.listen.tcp=9003 + --p2p.listen.udp=9003 + --p2p.scoring.peers=light + --p2p.ban.peers=true + --metrics.enabled + --metrics.addr=0.0.0.0 + --metrics.port=7300 + --pprof.enabled + --rpc.enable-admin + --safedb.path=/db + ports: + - "7145:8545" + - "9103:9003" + - "7100:7300" + - "6160:6060" + volumes: + - "safedb_a_data:/db" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + - "${PWD}/../.devnet-interop/genesis/l2/900200/rollup.json:/rollup.json" + - op_log_a:/op_log + env_file: + - "${PWD}/../.devnet-interop/env/l2/900200/op-node.env" + + op-node-b: + depends_on: + - l1 + - l1-bn + - l1-vc + - l2-b + - op-supervisor + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-node-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:devnet + command: > + op-node + --l1=ws://l1:8546 + --l1.beacon=http://l1-bn:5052 + --l1.epoch-poll-interval=12s + --l1.http-poll-interval=6s + --l2=http://l2-b:8551 + --l2.jwt-secret=/config/jwt-secret.txt + --supervisor=http://op-supervisor:8545 + --sequencer.enabled + --sequencer.l1-confs=0 + --verifier.l1-confs=0 + --rollup.config=/rollup.json + --rpc.addr=0.0.0.0 + --rpc.port=8545 + --p2p.listen.ip=0.0.0.0 + --p2p.listen.tcp=9003 + --p2p.listen.udp=9003 + --p2p.scoring.peers=light + --p2p.ban.peers=true + --metrics.enabled + --metrics.addr=0.0.0.0 + --metrics.port=7300 + --pprof.enabled + --rpc.enable-admin + --safedb.path=/db + ports: + - "7245:8545" + - "9203:9003" + - "7200:7300" + - "6260:6060" + volumes: + - "safedb_b_data:/db" + - "${PWD}/../ops-bedrock/test-jwt-secret.txt:/config/jwt-secret.txt" + - "${PWD}/../.devnet-interop/genesis/l2/900201/rollup.json:/rollup.json" + - op_log_b:/op_log + env_file: + - "${PWD}/../.devnet-interop/env/l2/900201/op-node.env" + + op-proposer-a: + depends_on: + - l1 + - op-node-a + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-proposer-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-proposer:devnet + ports: + - "6162:6060" + - "7102:7300" + - "6146:8545" + environment: + OP_PROPOSER_L1_ETH_RPC: http://l1:8545 + OP_PROPOSER_ROLLUP_RPC: http://op-node-a:8545 + OP_PROPOSER_POLL_INTERVAL: 1s + OP_PROPOSER_NUM_CONFIRMATIONS: 1 + OP_PROPOSER_GAME_TYPE: "254" + OP_PROPOSER_PROPOSAL_INTERVAL: "12s" + OP_PROPOSER_PPROF_ENABLED: "true" + OP_PROPOSER_METRICS_ENABLED: "true" + OP_PROPOSER_ALLOW_NON_FINALIZED: "true" + OP_PROPOSER_RPC_ENABLE_ADMIN: "true" + env_file: + - "${PWD}/../.devnet-interop/env/l2/900200/op-proposer.env" + + op-proposer-b: + depends_on: + - l1 + - op-node-b + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-proposer-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-proposer:devnet + ports: + - "6262:6060" + - "7202:7300" + - "6246:8545" + environment: + OP_PROPOSER_L1_ETH_RPC: http://l1:8545 + OP_PROPOSER_ROLLUP_RPC: http://op-node-b:8545 + OP_PROPOSER_POLL_INTERVAL: 1s + OP_PROPOSER_NUM_CONFIRMATIONS: 1 + OP_PROPOSER_GAME_TYPE: "254" + OP_PROPOSER_PROPOSAL_INTERVAL: "12s" + OP_PROPOSER_PPROF_ENABLED: "true" + OP_PROPOSER_METRICS_ENABLED: "true" + OP_PROPOSER_ALLOW_NON_FINALIZED: "true" + OP_PROPOSER_RPC_ENABLE_ADMIN: "true" + env_file: + - "${PWD}/../.devnet-interop/env/l2/900201/op-proposer.env" + + op-batcher-a: + depends_on: + - l1 + - l2-a + - op-node-a + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-batcher-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-batcher:devnet + ports: + - "6161:6060" + - "7101:7300" + - "6145:8545" + environment: + OP_BATCHER_L1_ETH_RPC: http://l1:8545 + OP_BATCHER_L2_ETH_RPC: http://l2-a:8545 + OP_BATCHER_ROLLUP_RPC: http://op-node-a:8545 + OP_BATCHER_MAX_CHANNEL_DURATION: 2 + OP_BATCHER_SUB_SAFETY_MARGIN: 4 # SWS is 15, ChannelTimeout is 40 + OP_BATCHER_POLL_INTERVAL: 1s + OP_BATCHER_NUM_CONFIRMATIONS: 1 + OP_BATCHER_PPROF_ENABLED: "true" + OP_BATCHER_METRICS_ENABLED: "true" + OP_BATCHER_RPC_ENABLE_ADMIN: "true" + OP_BATCHER_BATCH_TYPE: + # uncomment to use blobs + # OP_BATCHER_DATA_AVAILABILITY_TYPE: blobs + env_file: + - "${PWD}/../.devnet-interop/env/l2/900200/op-batcher.env" + + op-batcher-b: + depends_on: + - l1 + - l2-b + - op-node-b + build: + context: ../ + dockerfile: ops/docker/op-stack-go/Dockerfile + target: op-batcher-target + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-batcher:devnet + ports: + - "6261:6060" + - "7201:7300" + - "6245:8545" + environment: + OP_BATCHER_L1_ETH_RPC: http://l1:8545 + OP_BATCHER_L2_ETH_RPC: http://l2-b:8545 + OP_BATCHER_ROLLUP_RPC: http://op-node-b:8545 + OP_BATCHER_MAX_CHANNEL_DURATION: 2 + OP_BATCHER_SUB_SAFETY_MARGIN: 4 # SWS is 15, ChannelTimeout is 40 + OP_BATCHER_POLL_INTERVAL: 1s + OP_BATCHER_NUM_CONFIRMATIONS: 1 + OP_BATCHER_PPROF_ENABLED: "true" + OP_BATCHER_METRICS_ENABLED: "true" + OP_BATCHER_RPC_ENABLE_ADMIN: "true" + OP_BATCHER_BATCH_TYPE: + # uncomment to use blobs + # OP_BATCHER_DATA_AVAILABILITY_TYPE: blobs + env_file: + - "${PWD}/../.devnet-interop/env/l2/900201/op-batcher.env" diff --git a/interop-devnet/justfile b/interop-devnet/justfile new file mode 100644 index 000000000000..7aa215dbbd7f --- /dev/null +++ b/interop-devnet/justfile @@ -0,0 +1,33 @@ + +devnet-setup: + bash create-chains.sh + +devnet-build-images: + PWD="$(pwd)" DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 \ + docker compose build --progress plain \ + --build-arg GIT_COMMIT={git_commit} \ + --build-arg GIT_DATE={git_date} + +devnet-up: + docker compose up -d l1 l1-bn l1-vc + + docker compose up -d \ + op-supervisor \ + op-node-a op-batcher-a op-proposer-a \ + op-node-b op-batcher-b op-proposer-b + +devnet-down: + # stops services, does not remove containers/networks + docker compose stop + +devnet-clean: + rm -rf ../.devnet-interop + # Stops services, and removes containers/networks + docker compose down + # Now manually clean up the related images and volumes + # Note: `justfile` interprets the curly brackets. So we escape them, by wrapping it with more, as a string, like Jinja2. + docker image ls 'interop-devnet*' --format='{{ '{{.Repository}}' }}' | xargs -r docker rmi + docker volume ls --filter name=interop-devnet --format='{{ '{{.Name}}' }}' | xargs -r docker volume rm + +devnet-logs: + docker compose logs -f diff --git a/op-chain-ops/devkeys/devkeys.go b/op-chain-ops/devkeys/devkeys.go index d0526c8c1d3a..fdb151e4e6fc 100644 --- a/op-chain-ops/devkeys/devkeys.go +++ b/op-chain-ops/devkeys/devkeys.go @@ -93,6 +93,21 @@ func (role SuperchainOperatorRole) Key(chainID *big.Int) Key { } } +func (role *SuperchainOperatorRole) UnmarshalText(data []byte) error { + v := string(data) + for i := SuperchainOperatorRole(0); i < 20; i++ { + if i.String() == v { + *role = i + return nil + } + } + return fmt.Errorf("unknown superchain operator role %q", v) +} + +func (role *SuperchainOperatorRole) MarshalText() ([]byte, error) { + return []byte(role.String()), nil +} + // SuperchainOperatorKey is an account specific to an OperationRole of a given OP-Stack chain. type SuperchainOperatorKey struct { ChainID *big.Int @@ -181,6 +196,21 @@ func (role ChainOperatorRole) Key(chainID *big.Int) Key { } } +func (role *ChainOperatorRole) UnmarshalText(data []byte) error { + v := string(data) + for i := ChainOperatorRole(0); i < 20; i++ { + if i.String() == v { + *role = i + return nil + } + } + return fmt.Errorf("unknown chain operator role %q", v) +} + +func (role *ChainOperatorRole) MarshalText() ([]byte, error) { + return []byte(role.String()), nil +} + // ChainOperatorKey is an account specific to an OperationRole of a given OP-Stack chain. type ChainOperatorKey struct { ChainID *big.Int diff --git a/op-node/cmd/interop/interop.go b/op-node/cmd/interop/interop.go new file mode 100644 index 000000000000..3e6f75d530bb --- /dev/null +++ b/op-node/cmd/interop/interop.go @@ -0,0 +1,299 @@ +package interop + +import ( + "fmt" + "math/big" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/urfave/cli/v2" + + "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" + op_service "github.com/ethereum-optimism/optimism/op-service" + "github.com/ethereum-optimism/optimism/op-service/cliapp" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum/go-ethereum/crypto" +) + +var EnvPrefix = "OP_INTEROP" + +var ( + l1ChainIDFlag = &cli.Uint64Flag{ + Name: "l1.chainid", + Value: 900100, + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "L1_CHAINID"), + } + l2ChainIDsFlag = &cli.Uint64SliceFlag{ + Name: "l2.chainids", + Value: cli.NewUint64Slice(900200, 900201), + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "L2_CHAINIDS"), + } + timestampFlag = &cli.Uint64Flag{ + Name: "timestamp", + Value: 0, + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "TIMESTAMP"), + Usage: "Will use current timestamp, plus 5 seconds, if not set", + } + artifactsDirFlag = &cli.StringFlag{ + Name: "artifacts-dir", + Value: "packages/contracts-bedrock/forge-artifacts", + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "ARTIFACTS_DIR"), + } + foundryDirFlag = &cli.StringFlag{ + Name: "foundry-dir", + Value: "packages/contracts-bedrock", + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "FOUNDRY_DIR"), + Usage: "Optional, for source-map info during genesis generation", + } + outDirFlag = &cli.StringFlag{ + Name: "out-dir", + Value: ".interop-devnet", + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "OUT_DIR"), + } + // used in both dev-setup and devkey commands + mnemonicFlag = &cli.StringFlag{ + Name: "mnemonic", + Value: devkeys.TestMnemonic, + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "MNEMONIC"), + } + // for devkey command + devkeyDomainFlag = &cli.StringFlag{ + Name: "domain", + Value: "chain-operator", + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "DEVKEY_DOMAIN"), + } + devkeyChainIdFlag = &cli.Uint64Flag{ + Name: "chainid", + Value: 0, + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "DEVKEY_CHAINID"), + } + devkeyNameFlag = &cli.StringFlag{ + Name: "name", + EnvVars: op_service.PrefixEnvVar(EnvPrefix, "DEVKEY_NAME"), + } +) + +var InteropDevSetup = &cli.Command{ + Name: "dev-setup", + Usage: "Generate devnet genesis configs with one L1 and multiple L2s", + Flags: cliapp.ProtectFlags(append([]cli.Flag{ + l1ChainIDFlag, + l2ChainIDsFlag, + timestampFlag, + mnemonicFlag, + artifactsDirFlag, + foundryDirFlag, + outDirFlag, + }, oplog.CLIFlags(EnvPrefix)...)), + Action: func(cliCtx *cli.Context) error { + logCfg := oplog.ReadCLIConfig(cliCtx) + logger := oplog.NewLogger(cliCtx.App.Writer, logCfg) + + recipe := &interopgen.InteropDevRecipe{ + L1ChainID: cliCtx.Uint64(l1ChainIDFlag.Name), + L2ChainIDs: cliCtx.Uint64Slice(l2ChainIDsFlag.Name), + GenesisTimestamp: cliCtx.Uint64(timestampFlag.Name), + } + if recipe.GenesisTimestamp == 0 { + recipe.GenesisTimestamp = uint64(time.Now().Unix() + 5) + } + mnemonic := strings.TrimSpace(cliCtx.String(mnemonicFlag.Name)) + if mnemonic == devkeys.TestMnemonic { + logger.Warn("Using default test mnemonic!") + } + keys, err := devkeys.NewMnemonicDevKeys(mnemonic) + if err != nil { + return fmt.Errorf("failed to setup dev keys from mnemonic: %w", err) + } + worldCfg, err := recipe.Build(keys) + if err != nil { + return fmt.Errorf("failed to build deploy configs from interop recipe: %w", err) + } + if err := worldCfg.Check(logger); err != nil { + return fmt.Errorf("invalid deploy configs: %w", err) + } + artifactsDir := cliCtx.String(artifactsDirFlag.Name) + af := foundry.OpenArtifactsDir(artifactsDir) + var srcFs *foundry.SourceMapFS + if cliCtx.IsSet(foundryDirFlag.Name) { + srcDir := cliCtx.String(foundryDirFlag.Name) + srcFs = foundry.NewSourceMapFS(os.DirFS(srcDir)) + } + worldDeployment, worldOutput, err := interopgen.Deploy(logger, af, srcFs, worldCfg) + if err != nil { + return fmt.Errorf("failed to deploy interop dev setup: %w", err) + } + outDir := cliCtx.String(outDirFlag.Name) + // Write deployments + { + deploymentsDir := filepath.Join(outDir, "deployments") + l1Dir := filepath.Join(deploymentsDir, "l1") + if err := writeJson(filepath.Join(l1Dir, "common.json"), worldDeployment.L1); err != nil { + return fmt.Errorf("failed to write L1 deployment data: %w", err) + } + if err := writeJson(filepath.Join(l1Dir, "superchain.json"), worldDeployment.Superchain); err != nil { + return fmt.Errorf("failed to write Superchain deployment data: %w", err) + } + l2sDir := filepath.Join(deploymentsDir, "l2") + for id, dep := range worldDeployment.L2s { + l2Dir := filepath.Join(l2sDir, id) + if err := writeJson(filepath.Join(l2Dir, "addresses.json"), dep); err != nil { + return fmt.Errorf("failed to write L2 %s deployment data: %w", id, err) + } + } + } + // write genesis + { + genesisDir := filepath.Join(outDir, "genesis") + l1Dir := filepath.Join(genesisDir, "l1") + if err := writeJson(filepath.Join(l1Dir, "genesis.json"), worldOutput.L1.Genesis); err != nil { + return fmt.Errorf("failed to write L1 genesis data: %w", err) + } + l2sDir := filepath.Join(genesisDir, "l2") + for id, dep := range worldOutput.L2s { + l2Dir := filepath.Join(l2sDir, id) + if err := writeJson(filepath.Join(l2Dir, "genesis.json"), dep.Genesis); err != nil { + return fmt.Errorf("failed to write L2 %s genesis config: %w", id, err) + } + if err := writeJson(filepath.Join(l2Dir, "rollup.json"), dep.RollupCfg); err != nil { + return fmt.Errorf("failed to write L2 %s rollup config: %w", id, err) + } + } + } + return nil + }, +} + +func writeJson(path string, content any) error { + return jsonutil.WriteJSON[any](content, ioutil.ToBasicFile(path, 0o755)) +} + +var DevKeySecretCmd = &cli.Command{ + Name: "secret", + Usage: "Retrieve devkey secret, by specifying domain, chain ID, name.", + Flags: cliapp.ProtectFlags([]cli.Flag{ + mnemonicFlag, + devkeyDomainFlag, + devkeyChainIdFlag, + devkeyNameFlag, + }), + Action: func(context *cli.Context) error { + mnemonic := context.String(mnemonicFlag.Name) + domain := context.String(devkeyDomainFlag.Name) + chainID := context.Uint64(devkeyChainIdFlag.Name) + chainIDBig := new(big.Int).SetUint64(chainID) + name := context.String(devkeyNameFlag.Name) + k, err := parseKey(domain, chainIDBig, name) + if err != nil { + return err + } + mnemonicKeys, err := devkeys.NewMnemonicDevKeys(mnemonic) + if err != nil { + return err + } + secret, err := mnemonicKeys.Secret(k) + if err != nil { + return err + } + secretBin := crypto.FromECDSA(secret) + _, err = fmt.Fprintf(context.App.Writer, "%x", secretBin) + if err != nil { + return fmt.Errorf("failed to output secret key: %w", err) + } + return nil + }, +} + +var DevKeyAddressCmd = &cli.Command{ + Name: "address", + Usage: "Retrieve devkey address, by specifying domain, chain ID, name.", + Flags: cliapp.ProtectFlags([]cli.Flag{ + mnemonicFlag, + devkeyDomainFlag, + devkeyChainIdFlag, + devkeyNameFlag, + }), + Action: func(context *cli.Context) error { + mnemonic := context.String(mnemonicFlag.Name) + domain := context.String(devkeyDomainFlag.Name) + chainID := context.Uint64(devkeyChainIdFlag.Name) + chainIDBig := new(big.Int).SetUint64(chainID) + name := context.String(devkeyNameFlag.Name) + k, err := parseKey(domain, chainIDBig, name) + if err != nil { + return err + } + mnemonicKeys, err := devkeys.NewMnemonicDevKeys(mnemonic) + if err != nil { + return err + } + addr, err := mnemonicKeys.Address(k) + if err != nil { + return err + } + _, err = fmt.Fprintf(context.App.Writer, "%s", addr) + if err != nil { + return fmt.Errorf("failed to output address: %w", err) + } + return nil + }, +} + +var DevKeyCmd = &cli.Command{ + Name: "devkey", + Usage: "Retrieve devkey secret or address", + Subcommands: cli.Commands{ + DevKeySecretCmd, + DevKeyAddressCmd, + }, +} + +func parseKey(domain string, chainID *big.Int, name string) (devkeys.Key, error) { + switch domain { + case "user": + index, err := strconv.ParseUint(name, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse user index: %w", err) + } + return devkeys.ChainUserKey{ + ChainID: chainID, + Index: index, + }, nil + case "chain-operator": + var role devkeys.ChainOperatorRole + if err := role.UnmarshalText([]byte(name)); err != nil { + return nil, fmt.Errorf("failed to parse chain operator role: %w", err) + } + return devkeys.ChainOperatorKey{ + ChainID: chainID, + Role: role, + }, nil + case "superchain-operator": + var role devkeys.SuperchainOperatorRole + if err := role.UnmarshalText([]byte(name)); err != nil { + return nil, fmt.Errorf("failed to parse chain operator role: %w", err) + } + return devkeys.SuperchainOperatorKey{ + ChainID: chainID, + Role: role, + }, nil + default: + return nil, fmt.Errorf("unknown devkey domain %q", domain) + } +} + +var InteropCmd = &cli.Command{ + Name: "interop", + Usage: "Experimental tools for OP-Stack interop networks.", + Subcommands: cli.Commands{ + InteropDevSetup, + DevKeyCmd, + }, +} diff --git a/op-node/cmd/main.go b/op-node/cmd/main.go index 8f6688b51cbf..b82b3f6babce 100644 --- a/op-node/cmd/main.go +++ b/op-node/cmd/main.go @@ -12,6 +12,7 @@ import ( opnode "github.com/ethereum-optimism/optimism/op-node" "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/cmd/genesis" + "github.com/ethereum-optimism/optimism/op-node/cmd/interop" "github.com/ethereum-optimism/optimism/op-node/cmd/networks" "github.com/ethereum-optimism/optimism/op-node/cmd/p2p" "github.com/ethereum-optimism/optimism/op-node/flags" @@ -62,6 +63,7 @@ func main() { Name: "networks", Subcommands: networks.Subcommands, }, + interop.InteropCmd, } ctx := ctxinterrupt.WithSignalWaiterMain(context.Background()) diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 54334c150296..de94b59c0e04 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -73,6 +73,7 @@ var ( EnvVars: prefixEnvVars("L1_BEACON"), Category: RollupCategory, } + /* Optional Flags */ SupervisorAddr = &cli.StringFlag{ Name: "supervisor", Usage: "RPC address of interop supervisor service for cross-chain safety verification." + @@ -80,7 +81,6 @@ var ( Hidden: true, // hidden for now during early testing. EnvVars: prefixEnvVars("SUPERVISOR"), } - /* Optional Flags */ BeaconHeader = &cli.StringFlag{ Name: "l1.beacon-header", Usage: "Optional HTTP header to add to all requests to the L1 Beacon endpoint. Format: 'X-Key: Value'", diff --git a/op-node/rollup/interop/interop.go b/op-node/rollup/interop/interop.go index c929c8f0d01d..a4342b6a19f6 100644 --- a/op-node/rollup/interop/interop.go +++ b/op-node/rollup/interop/interop.go @@ -27,7 +27,7 @@ type InteropBackend interface { SafeView(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) - DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) + CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.L1BlockRef, lastDerived eth.BlockRef) error @@ -232,10 +232,11 @@ func (d *InteropDeriver) onCrossSafeUpdateEvent(x engine.CrossSafeUpdateEvent) e Hash: result.Cross.Hash, Number: result.Cross.Number, } - derivedFrom, err := d.backend.DerivedFrom(ctx, d.chainID, derived) + derivedFrom, err := d.backend.CrossDerivedFrom(ctx, d.chainID, derived) if err != nil { return fmt.Errorf("failed to get derived-from of %s: %w", result.Cross, err) } + d.log.Info("New cross-safe block", "block", result.Cross.Number) ref, err := d.l2.L2BlockRefByHash(ctx, result.Cross.Hash) if err != nil { return fmt.Errorf("failed to get block ref of %s: %w", result.Cross, err) @@ -272,6 +273,7 @@ func (d *InteropDeriver) onFinalizedUpdate(x engine.FinalizedUpdateEvent) error if err != nil { return fmt.Errorf("failed to get block ref of %s: %w", finalized, err) } + d.log.Info("New finalized block from supervisor", "block", finalized.Number) d.emitter.Emit(engine.PromoteFinalizedEvent{ Ref: ref, }) diff --git a/op-service/ioutil/streams.go b/op-service/ioutil/streams.go index 91f122906db0..c35aefa202ef 100644 --- a/op-service/ioutil/streams.go +++ b/op-service/ioutil/streams.go @@ -1,8 +1,10 @@ package ioutil import ( + "fmt" "io" "os" + "path/filepath" ) var ( @@ -21,6 +23,20 @@ func NoOutputStream() OutputTarget { } } +func ToBasicFile(path string, perm os.FileMode) OutputTarget { + return func() (io.Writer, io.Closer, Aborter, error) { + outDir := filepath.Dir(path) + if err := os.MkdirAll(outDir, perm); err != nil { + return nil, nil, nil, fmt.Errorf("failed to create dir %q: %w", outDir, err) + } + f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perm) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to open %q: %w", path, err) + } + return f, f, func() {}, nil + } +} + func ToAtomicFile(path string, perm os.FileMode) OutputTarget { return func() (io.Writer, io.Closer, Aborter, error) { f, err := NewAtomicWriterCompressed(path, perm) diff --git a/op-service/jsonutil/json.go b/op-service/jsonutil/json.go index 5993138595b4..8549c170d42a 100644 --- a/op-service/jsonutil/json.go +++ b/op-service/jsonutil/json.go @@ -67,6 +67,7 @@ type jsonEncoder struct { func newJSONEncoder(w io.Writer) Encoder { e := json.NewEncoder(w) e.SetIndent("", " ") + e.SetEscapeHTML(false) return &jsonEncoder{ e: e, } diff --git a/op-service/locks/rwmap.go b/op-service/locks/rwmap.go new file mode 100644 index 000000000000..779e3554c824 --- /dev/null +++ b/op-service/locks/rwmap.go @@ -0,0 +1,48 @@ +package locks + +import "sync" + +// RWMap is a simple wrapper around a map, with global Read-Write protection. +// For many concurrent reads/writes a sync.Map may be more performant, +// although it does not utilize Go generics. +// The RWMap does not have to be initialized, +// it is immediately ready for reads/writes. +type RWMap[K comparable, V any] struct { + inner map[K]V + mu sync.RWMutex +} + +func (m *RWMap[K, V]) Has(key K) (ok bool) { + m.mu.RLock() + defer m.mu.RUnlock() + _, ok = m.inner[key] + return +} + +func (m *RWMap[K, V]) Get(key K) (value V, ok bool) { + m.mu.RLock() + defer m.mu.RUnlock() + value, ok = m.inner[key] + return +} + +func (m *RWMap[K, V]) Set(key K, value V) { + m.mu.Lock() + defer m.mu.Unlock() + if m.inner == nil { + m.inner = make(map[K]V) + } + m.inner[key] = value +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +func (m *RWMap[K, V]) Range(f func(key K, value V) bool) { + m.mu.RLock() + defer m.mu.RUnlock() + for k, v := range m.inner { + if !f(k, v) { + break + } + } +} diff --git a/op-service/locks/rwmap_test.go b/op-service/locks/rwmap_test.go new file mode 100644 index 000000000000..c78fab97034b --- /dev/null +++ b/op-service/locks/rwmap_test.go @@ -0,0 +1,52 @@ +package locks + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRWMap(t *testing.T) { + m := &RWMap[uint64, int64]{} + + // get on new map + v, ok := m.Get(123) + require.False(t, ok) + require.Equal(t, int64(0), v) + + // set a value + m.Set(123, 42) + v, ok = m.Get(123) + require.True(t, ok) + require.Equal(t, int64(42), v) + + // overwrite a value + m.Set(123, -42) + v, ok = m.Get(123) + require.True(t, ok) + require.Equal(t, int64(-42), v) + + // add a value + m.Set(10, 100) + + // range over values + got := make(map[uint64]int64) + m.Range(func(key uint64, value int64) bool { + if _, ok := got[key]; ok { + panic("duplicate") + } + got[key] = value + return true + }) + require.Len(t, got, 2) + require.Equal(t, int64(100), got[uint64(10)]) + require.Equal(t, int64(-42), got[uint64(123)]) + + // range and stop early + clear(got) + m.Range(func(key uint64, value int64) bool { + got[key] = value + return false + }) + require.Len(t, got, 1, "stop early") +} diff --git a/op-service/locks/rwvalue.go b/op-service/locks/rwvalue.go new file mode 100644 index 000000000000..12ca65e61d73 --- /dev/null +++ b/op-service/locks/rwvalue.go @@ -0,0 +1,24 @@ +package locks + +import "sync" + +// RWValue is a simple container struct, to deconflict reads/writes of the value, +// without locking up a bigger structure in the caller. +// It exposes the underlying RWLock and Value for direct access where needed. +type RWValue[E any] struct { + sync.RWMutex + Value E +} + +func (c *RWValue[E]) Get() (out E) { + c.RLock() + defer c.RUnlock() + out = c.Value + return +} + +func (c *RWValue[E]) Set(v E) { + c.Lock() + defer c.Unlock() + c.Value = v +} diff --git a/op-service/locks/rwvalue_test.go b/op-service/locks/rwvalue_test.go new file mode 100644 index 000000000000..f99d9345a1cc --- /dev/null +++ b/op-service/locks/rwvalue_test.go @@ -0,0 +1,16 @@ +package locks + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRWValue(t *testing.T) { + v := &RWValue[uint64]{} + require.Equal(t, uint64(0), v.Get()) + v.Set(123) + require.Equal(t, uint64(123), v.Get()) + v.Set(42) + require.Equal(t, uint64(42), v.Get()) +} diff --git a/op-service/sources/supervisor_client.go b/op-service/sources/supervisor_client.go index ba8663c335ce..d6191b9cfb20 100644 --- a/op-service/sources/supervisor_client.go +++ b/op-service/sources/supervisor_client.go @@ -114,12 +114,12 @@ func (cl *SupervisorClient) Finalized(ctx context.Context, chainID types.ChainID return result, err } -func (cl *SupervisorClient) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.BlockRef, error) { +func (cl *SupervisorClient) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.BlockRef, error) { var result eth.BlockRef err := cl.client.CallContext( ctx, &result, - "supervisor_derivedFrom", + "supervisor_crossDerivedFrom", chainID, derived) return result, err diff --git a/op-service/testutils/fake_interop_backend.go b/op-service/testutils/fake_interop_backend.go index 4ef439a9a3bc..4c8624d53552 100644 --- a/op-service/testutils/fake_interop_backend.go +++ b/op-service/testutils/fake_interop_backend.go @@ -29,7 +29,7 @@ func (m *FakeInteropBackend) Finalized(ctx context.Context, chainID types.ChainI return m.FinalizedFn(ctx, chainID) } -func (m *FakeInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { +func (m *FakeInteropBackend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { return m.DerivedFromFn(ctx, chainID, derived) } diff --git a/op-service/testutils/mock_interop_backend.go b/op-service/testutils/mock_interop_backend.go index 6724acedd43f..af6762c204cd 100644 --- a/op-service/testutils/mock_interop_backend.go +++ b/op-service/testutils/mock_interop_backend.go @@ -58,13 +58,13 @@ func (m *MockInteropBackend) ExpectFinalized(chainID types.ChainID, result eth.B m.Mock.On("Finalized", chainID).Once().Return(result, &err) } -func (m *MockInteropBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { - result := m.Mock.MethodCalled("DerivedFrom", chainID, derived) +func (m *MockInteropBackend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (eth.L1BlockRef, error) { + result := m.Mock.MethodCalled("CrossDerivedFrom", chainID, derived) return result.Get(0).(eth.L1BlockRef), *result.Get(1).(*error) } func (m *MockInteropBackend) ExpectDerivedFrom(chainID types.ChainID, derived eth.BlockID, result eth.L1BlockRef, err error) { - m.Mock.On("DerivedFrom", chainID, derived).Once().Return(result, &err) + m.Mock.On("CrossDerivedFrom", chainID, derived).Once().Return(result, &err) } func (m *MockInteropBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index ff5d5575cd31..d4328f6932c7 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -402,11 +402,11 @@ func (su *SupervisorBackend) Finalized(ctx context.Context, chainID types.ChainI return v.ID(), nil } -func (su *SupervisorBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { +func (su *SupervisorBackend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { su.mu.RLock() defer su.mu.RUnlock() - v, err := su.chainDBs.DerivedFrom(chainID, derived) + v, err := su.chainDBs.CrossDerivedFromBlockRef(chainID, derived) if err != nil { return eth.BlockRef{}, err } diff --git a/op-supervisor/supervisor/backend/cross/safe_start_test.go b/op-supervisor/supervisor/backend/cross/safe_start_test.go index cb6bd4757214..1a5924e06963 100644 --- a/op-supervisor/supervisor/backend/cross/safe_start_test.go +++ b/op-supervisor/supervisor/backend/cross/safe_start_test.go @@ -223,7 +223,7 @@ func TestCrossSafeHazards(t *testing.T) { require.ErrorContains(t, err, "some error") require.Empty(t, hazards) }) - t.Run("timestamp is less, DerivedFrom returns error", func(t *testing.T) { + t.Run("timestamp is less, CrossDerivedFrom returns error", func(t *testing.T) { ssd := &mockSafeStartDeps{} sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} ssd.checkFn = func() (includedIn types.BlockSeal, err error) { @@ -245,7 +245,7 @@ func TestCrossSafeHazards(t *testing.T) { require.ErrorContains(t, err, "some error") require.Empty(t, hazards) }) - t.Run("timestamp is less, DerivedFrom Number is greater", func(t *testing.T) { + t.Run("timestamp is less, CrossDerivedFrom Number is greater", func(t *testing.T) { ssd := &mockSafeStartDeps{} sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} ssd.checkFn = func() (includedIn types.BlockSeal, err error) { @@ -268,7 +268,7 @@ func TestCrossSafeHazards(t *testing.T) { require.ErrorIs(t, err, types.ErrOutOfScope) require.Empty(t, hazards) }) - t.Run("timestamp is less, DerivedFrom Number less", func(t *testing.T) { + t.Run("timestamp is less, CrossDerivedFrom Number less", func(t *testing.T) { ssd := &mockSafeStartDeps{} sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} ssd.checkFn = func() (includedIn types.BlockSeal, err error) { @@ -291,7 +291,7 @@ func TestCrossSafeHazards(t *testing.T) { require.NoError(t, err) require.Empty(t, hazards) }) - t.Run("timestamp is less, DerivedFrom Number equal", func(t *testing.T) { + t.Run("timestamp is less, CrossDerivedFrom Number equal", func(t *testing.T) { ssd := &mockSafeStartDeps{} sampleBlockSeal := types.BlockSeal{Number: 3, Hash: common.BytesToHash([]byte{0x02})} ssd.checkFn = func() (includedIn types.BlockSeal, err error) { diff --git a/op-supervisor/supervisor/backend/cross/worker.go b/op-supervisor/supervisor/backend/cross/worker.go index 1342d7048fab..b80c78442c6b 100644 --- a/op-supervisor/supervisor/backend/cross/worker.go +++ b/op-supervisor/supervisor/backend/cross/worker.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" "github.com/ethereum/go-ethereum/log" ) @@ -37,7 +38,7 @@ func NewWorker(log log.Logger, workFn workFn) *Worker { log: log, poke: make(chan struct{}, 1), // The data may have changed, and we may have missed a poke, so re-attempt regularly. - pollDuration: time.Second * 4, + pollDuration: 250 * time.Millisecond, ctx: ctx, cancel: cancel, } @@ -69,7 +70,11 @@ func (s *Worker) worker() { if errors.Is(err, s.ctx.Err()) { return } - s.log.Error("Failed to process work", "err", err) + if errors.Is(err, types.ErrFuture) { + s.log.Debug("Failed to process work", "err", err) + } else { + s.log.Warn("Failed to process work", "err", err) + } } // await next time we process, or detect shutdown diff --git a/op-supervisor/supervisor/backend/db/db.go b/op-supervisor/supervisor/backend/db/db.go index 922f849dea05..b667718759b7 100644 --- a/op-supervisor/supervisor/backend/db/db.go +++ b/op-supervisor/supervisor/backend/db/db.go @@ -4,12 +4,12 @@ import ( "errors" "fmt" "io" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/locks" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/fromda" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/logs" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" @@ -73,28 +73,23 @@ var _ LogStorage = (*logs.DB)(nil) // ChainsDB is a database that stores logs and derived-from data for multiple chains. // it implements the LogStorage interface, as well as several DB interfaces needed by the cross package. type ChainsDB struct { - // RW mutex: - // Read = chains can be read / mutated. - // Write = set of chains is changing. - mu sync.RWMutex - // unsafe info: the sequence of block seals and events - logDBs map[types.ChainID]LogStorage + logDBs locks.RWMap[types.ChainID, LogStorage] // cross-unsafe: how far we have processed the unsafe data. // If present but set to a zeroed value the cross-unsafe will fallback to cross-safe. - crossUnsafe map[types.ChainID]types.BlockSeal + crossUnsafe locks.RWMap[types.ChainID, *locks.RWValue[types.BlockSeal]] // local-safe: index of what we optimistically know about L2 blocks being derived from L1 - localDBs map[types.ChainID]LocalDerivedFromStorage + localDBs locks.RWMap[types.ChainID, LocalDerivedFromStorage] // cross-safe: index of L2 blocks we know to only have cross-L2 valid dependencies - crossDBs map[types.ChainID]CrossDerivedFromStorage + crossDBs locks.RWMap[types.ChainID, CrossDerivedFromStorage] // finalized: the L1 finality progress. This can be translated into what may be considered as finalized in L2. // It is initially zeroed, and the L2 finality query will return // an error until it has this L1 finality to work with. - finalizedL1 eth.L1BlockRef + finalizedL1 locks.RWValue[eth.L1BlockRef] // depSet is the dependency set, used to determine what may be tracked, // what is missing, and to provide it to DB users. @@ -105,78 +100,62 @@ type ChainsDB struct { func NewChainsDB(l log.Logger, depSet depset.DependencySet) *ChainsDB { return &ChainsDB{ - logDBs: make(map[types.ChainID]LogStorage), - logger: l, - localDBs: make(map[types.ChainID]LocalDerivedFromStorage), - crossDBs: make(map[types.ChainID]CrossDerivedFromStorage), - crossUnsafe: make(map[types.ChainID]types.BlockSeal), - depSet: depSet, + logger: l, + depSet: depSet, } } func (db *ChainsDB) AddLogDB(chainID types.ChainID, logDB LogStorage) { - db.mu.Lock() - defer db.mu.Unlock() - - if _, ok := db.logDBs[chainID]; ok { + if db.logDBs.Has(chainID) { db.logger.Warn("overwriting existing log DB for chain", "chain", chainID) } - db.logDBs[chainID] = logDB + db.logDBs.Set(chainID, logDB) } func (db *ChainsDB) AddLocalDerivedFromDB(chainID types.ChainID, dfDB LocalDerivedFromStorage) { - db.mu.Lock() - defer db.mu.Unlock() - - if _, ok := db.localDBs[chainID]; ok { + if db.localDBs.Has(chainID) { db.logger.Warn("overwriting existing local derived-from DB for chain", "chain", chainID) } - db.localDBs[chainID] = dfDB + db.localDBs.Set(chainID, dfDB) } func (db *ChainsDB) AddCrossDerivedFromDB(chainID types.ChainID, dfDB CrossDerivedFromStorage) { - db.mu.Lock() - defer db.mu.Unlock() - - if _, ok := db.crossDBs[chainID]; ok { + if db.crossDBs.Has(chainID) { db.logger.Warn("overwriting existing cross derived-from DB for chain", "chain", chainID) } - db.crossDBs[chainID] = dfDB + db.crossDBs.Set(chainID, dfDB) } func (db *ChainsDB) AddCrossUnsafeTracker(chainID types.ChainID) { - db.mu.Lock() - defer db.mu.Unlock() - - if _, ok := db.crossUnsafe[chainID]; ok { + if db.crossUnsafe.Has(chainID) { db.logger.Warn("overwriting existing cross-unsafe tracker for chain", "chain", chainID) } - db.crossUnsafe[chainID] = types.BlockSeal{} + db.crossUnsafe.Set(chainID, &locks.RWValue[types.BlockSeal]{}) } // ResumeFromLastSealedBlock prepares the chains db to resume recording events after a restart. // It rewinds the database to the last block that is guaranteed to have been fully recorded to the database, // to ensure it can resume recording from the first log of the next block. func (db *ChainsDB) ResumeFromLastSealedBlock() error { - db.mu.RLock() - defer db.mu.RUnlock() - - for chain, logStore := range db.logDBs { + var result error + db.logDBs.Range(func(chain types.ChainID, logStore LogStorage) bool { headNum, ok := logStore.LatestSealedBlockNum() if !ok { // db must be empty, nothing to rewind to db.logger.Info("Resuming, but found no DB contents", "chain", chain) - continue + return true } db.logger.Info("Resuming, starting from last sealed block", "head", headNum) if err := logStore.Rewind(headNum); err != nil { - return fmt.Errorf("failed to rewind chain %s to sealed block %d", chain, headNum) + result = fmt.Errorf("failed to rewind chain %s to sealed block %d", chain, headNum) + return false } - } - return nil + return true + }) + return result } func (db *ChainsDB) DependencySet() depset.DependencySet { @@ -184,14 +163,12 @@ func (db *ChainsDB) DependencySet() depset.DependencySet { } func (db *ChainsDB) Close() error { - db.mu.Lock() - defer db.mu.Unlock() - var combined error - for id, logDB := range db.logDBs { + db.logDBs.Range(func(id types.ChainID, logDB LogStorage) bool { if err := logDB.Close(); err != nil { combined = errors.Join(combined, fmt.Errorf("failed to close log db for chain %v: %w", id, err)) } - } + return true + }) return combined } diff --git a/op-supervisor/supervisor/backend/db/fromda/update_test.go b/op-supervisor/supervisor/backend/db/fromda/update_test.go index 41be48ae0815..10b6b3bdd053 100644 --- a/op-supervisor/supervisor/backend/db/fromda/update_test.go +++ b/op-supervisor/supervisor/backend/db/fromda/update_test.go @@ -64,7 +64,7 @@ func TestBadUpdates(t *testing.T) { assertFn: noChange, }, { - name: "DerivedFrom with conflicting parent root, same L1 height, new L2: accepted, L1 parent-hash is used only on L1 increments.", + name: "CrossDerivedFrom with conflicting parent root, same L1 height, new L2: accepted, L1 parent-hash is used only on L1 increments.", setupFn: func(t *testing.T, db *DB, m *stubMetrics) { require.NoError(t, db.AddDerived(toRef(dDerivedFrom, common.Hash{0x42}), toRef(eDerived, dDerived.Hash)), types.ErrConflict) }, diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index 01e5ef0171c6..867e4e71955b 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -12,10 +12,7 @@ import ( ) func (db *ChainsDB) FindSealedBlock(chain types.ChainID, number uint64) (seal types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } @@ -26,10 +23,7 @@ func (db *ChainsDB) FindSealedBlock(chain types.ChainID, number uint64) (seal ty // for the given chain. It does not contain safety guarantees. // The block number might not be available (empty database, or non-existent chain). func (db *ChainsDB) LatestBlockNum(chain types.ChainID) (num uint64, ok bool) { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, knownChain := db.logDBs[chain] + logDB, knownChain := db.logDBs.Get(chain) if !knownChain { return 0, false } @@ -37,16 +31,15 @@ func (db *ChainsDB) LatestBlockNum(chain types.ChainID) (num uint64, ok bool) { } func (db *ChainsDB) IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error { - db.mu.RLock() - defer db.mu.RUnlock() - v, ok := db.crossUnsafe[chainID] + v, ok := db.crossUnsafe.Get(chainID) if !ok { return types.ErrUnknownChain } - if v == (types.BlockSeal{}) { + crossUnsafe := v.Get() + if crossUnsafe == (types.BlockSeal{}) { return types.ErrFuture } - if block.Number > v.Number { + if block.Number > crossUnsafe.Number { return types.ErrFuture } // TODO(#11693): make cross-unsafe reorg safe @@ -54,9 +47,7 @@ func (db *ChainsDB) IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) erro } func (db *ChainsDB) ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (parent eth.BlockID, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - logDB, ok := db.logDBs[chainID] + logDB, ok := db.logDBs.Get(chainID) if !ok { return eth.BlockID{}, types.ErrUnknownChain } @@ -72,9 +63,7 @@ func (db *ChainsDB) ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (pa } func (db *ChainsDB) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error { - db.mu.RLock() - defer db.mu.RUnlock() - logDB, ok := db.logDBs[chainID] + logDB, ok := db.logDBs.Get(chainID) if !ok { return types.ErrUnknownChain } @@ -89,10 +78,7 @@ func (db *ChainsDB) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) erro } func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) { - db.mu.RLock() - defer db.mu.RUnlock() - - eventsDB, ok := db.logDBs[chainID] + eventsDB, ok := db.logDBs.Get(chainID) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } @@ -104,29 +90,24 @@ func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) } func (db *ChainsDB) CrossUnsafe(chainID types.ChainID) (types.BlockSeal, error) { - db.mu.RLock() - defer db.mu.RUnlock() - - result, ok := db.crossUnsafe[chainID] + result, ok := db.crossUnsafe.Get(chainID) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } + crossUnsafe := result.Get() // Fall back to cross-safe if cross-unsafe is not known yet - if result == (types.BlockSeal{}) { + if crossUnsafe == (types.BlockSeal{}) { _, crossSafe, err := db.CrossSafe(chainID) if err != nil { return types.BlockSeal{}, fmt.Errorf("no cross-unsafe known for chain %s, and failed to fall back to cross-safe value: %w", chainID, err) } return crossSafe, nil } - return result, nil + return crossUnsafe, nil } func (db *ChainsDB) LocalSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - localDB, ok := db.localDBs[chainID] + localDB, ok := db.localDBs.Get(chainID) if !ok { return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain } @@ -134,10 +115,7 @@ func (db *ChainsDB) LocalSafe(chainID types.ChainID) (derivedFrom types.BlockSea } func (db *ChainsDB) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - crossDB, ok := db.crossDBs[chainID] + crossDB, ok := db.crossDBs.Get(chainID) if !ok { return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain } @@ -145,10 +123,7 @@ func (db *ChainsDB) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSea } func (db *ChainsDB) Finalized(chainID types.ChainID) (types.BlockSeal, error) { - db.mu.RLock() - defer db.mu.RUnlock() - - finalizedL1 := db.finalizedL1 + finalizedL1 := db.finalizedL1.Get() if finalizedL1 == (eth.L1BlockRef{}) { return types.BlockSeal{}, errors.New("no finalized L1 signal, cannot determine L2 finality yet") } @@ -160,26 +135,25 @@ func (db *ChainsDB) Finalized(chainID types.ChainID) (types.BlockSeal, error) { } func (db *ChainsDB) LastDerivedFrom(chainID types.ChainID, derivedFrom eth.BlockID) (derived types.BlockSeal, err error) { - crossDB, ok := db.crossDBs[chainID] + crossDB, ok := db.crossDBs.Get(chainID) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } return crossDB.LastDerivedAt(derivedFrom) } -func (db *ChainsDB) DerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - localDB, ok := db.localDBs[chainID] +// CrossDerivedFromBlockRef returns the block that the given block was derived from, if it exists in the cross derived-from storage. +// This includes the parent-block lookup. Use CrossDerivedFrom if no parent-block info is needed. +func (db *ChainsDB) CrossDerivedFromBlockRef(chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { + xdb, ok := db.crossDBs.Get(chainID) if !ok { return eth.BlockRef{}, types.ErrUnknownChain } - res, err := localDB.DerivedFrom(derived) + res, err := xdb.DerivedFrom(derived) if err != nil { return eth.BlockRef{}, err } - parent, err := localDB.PreviousDerivedFrom(res.ID()) + parent, err := xdb.PreviousDerivedFrom(res.ID()) if err != nil { return eth.BlockRef{}, err } @@ -189,10 +163,7 @@ func (db *ChainsDB) DerivedFrom(chainID types.ChainID, derived eth.BlockID) (der // Check calls the underlying logDB to determine if the given log entry exists at the given location. // If the block-seal of the block that includes the log is known, it is returned. It is fully zeroed otherwise, if the block is in-progress. func (db *ChainsDB) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } @@ -202,10 +173,7 @@ func (db *ChainsDB) Check(chain types.ChainID, blockNum uint64, logIdx uint32, l // OpenBlock returns the Executing Messages for the block at the given number on the given chain. // it routes the request to the appropriate logDB. func (db *ChainsDB) OpenBlock(chainID types.ChainID, blockNum uint64) (seal eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chainID] + logDB, ok := db.logDBs.Get(chainID) if !ok { return eth.BlockRef{}, 0, nil, types.ErrUnknownChain } @@ -215,10 +183,7 @@ func (db *ChainsDB) OpenBlock(chainID types.ChainID, blockNum uint64) (seal eth. // LocalDerivedFrom returns the block that the given block was derived from, if it exists in the local derived-from storage. // it routes the request to the appropriate localDB. func (db *ChainsDB) LocalDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - lDB, ok := db.localDBs[chain] + lDB, ok := db.localDBs.Get(chain) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } @@ -228,10 +193,7 @@ func (db *ChainsDB) LocalDerivedFrom(chain types.ChainID, derived eth.BlockID) ( // CrossDerivedFrom returns the block that the given block was derived from, if it exists in the cross derived-from storage. // it routes the request to the appropriate crossDB. func (db *ChainsDB) CrossDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - xDB, ok := db.crossDBs[chain] + xDB, ok := db.crossDBs.Get(chain) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } @@ -247,15 +209,12 @@ func (db *ChainsDB) CrossDerivedFrom(chain types.ChainID, derived eth.BlockID) ( // Or ErrOutOfScope, with non-zero derivedFromScope, // if additional L1 data is needed to cross-verify the candidate L2 block. func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - - xDB, ok := db.crossDBs[chain] + xDB, ok := db.crossDBs.Get(chain) if !ok { return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain } - lDB, ok := db.localDBs[chain] + lDB, ok := db.localDBs.Get(chain) if !ok { return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain } @@ -323,9 +282,7 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c } func (db *ChainsDB) PreviousDerived(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - lDB, ok := db.localDBs[chain] + lDB, ok := db.localDBs.Get(chain) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } @@ -333,9 +290,7 @@ func (db *ChainsDB) PreviousDerived(chain types.ChainID, derived eth.BlockID) (p } func (db *ChainsDB) PreviousDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (prevDerivedFrom types.BlockSeal, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - lDB, ok := db.localDBs[chain] + lDB, ok := db.localDBs.Get(chain) if !ok { return types.BlockSeal{}, types.ErrUnknownChain } @@ -343,9 +298,7 @@ func (db *ChainsDB) PreviousDerivedFrom(chain types.ChainID, derivedFrom eth.Blo } func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - lDB, ok := db.localDBs[chain] + lDB, ok := db.localDBs.Get(chain) if !ok { return eth.BlockRef{}, types.ErrUnknownChain } @@ -360,9 +313,6 @@ func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID // it assumes the log entry has already been checked and is valid, this function only checks safety levels. // Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest. func (db *ChainsDB) Safest(chainID types.ChainID, blockNum uint64, index uint32) (safest types.SafetyLevel, err error) { - db.mu.RLock() - defer db.mu.RUnlock() - if finalized, err := db.Finalized(chainID); err == nil { if finalized.Number >= blockNum { return types.Finalized, nil @@ -395,7 +345,7 @@ func (db *ChainsDB) Safest(chainID types.ChainID, blockNum uint64, index uint32) } func (db *ChainsDB) IteratorStartingAt(chain types.ChainID, sealedNum uint64, logIndex uint32) (logs.Iterator, error) { - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return nil, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain) } diff --git a/op-supervisor/supervisor/backend/db/update.go b/op-supervisor/supervisor/backend/db/update.go index e45fd3f469a5..932b19875575 100644 --- a/op-supervisor/supervisor/backend/db/update.go +++ b/op-supervisor/supervisor/backend/db/update.go @@ -15,10 +15,7 @@ func (db *ChainsDB) AddLog( parentBlock eth.BlockID, logIdx uint32, execMsg *types.ExecutingMessage) error { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return fmt.Errorf("cannot AddLog: %w: %v", types.ErrUnknownChain, chain) } @@ -26,10 +23,7 @@ func (db *ChainsDB) AddLog( } func (db *ChainsDB) SealBlock(chain types.ChainID, block eth.BlockRef) error { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return fmt.Errorf("cannot SealBlock: %w: %v", types.ErrUnknownChain, chain) } @@ -42,10 +36,7 @@ func (db *ChainsDB) SealBlock(chain types.ChainID, block eth.BlockRef) error { } func (db *ChainsDB) Rewind(chain types.ChainID, headBlockNum uint64) error { - db.mu.RLock() - defer db.mu.RUnlock() - - logDB, ok := db.logDBs[chain] + logDB, ok := db.logDBs.Get(chain) if !ok { return fmt.Errorf("cannot Rewind: %w: %s", types.ErrUnknownChain, chain) } @@ -53,10 +44,7 @@ func (db *ChainsDB) Rewind(chain types.ChainID, headBlockNum uint64) error { } func (db *ChainsDB) UpdateLocalSafe(chain types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { - db.mu.RLock() - defer db.mu.RUnlock() - - localDB, ok := db.localDBs[chain] + localDB, ok := db.localDBs.Get(chain) if !ok { return fmt.Errorf("cannot UpdateLocalSafe: %w: %v", types.ErrUnknownChain, chain) } @@ -65,22 +53,17 @@ func (db *ChainsDB) UpdateLocalSafe(chain types.ChainID, derivedFrom eth.BlockRe } func (db *ChainsDB) UpdateCrossUnsafe(chain types.ChainID, crossUnsafe types.BlockSeal) error { - db.mu.RLock() - defer db.mu.RUnlock() - - if _, ok := db.crossUnsafe[chain]; !ok { + v, ok := db.crossUnsafe.Get(chain) + if !ok { return fmt.Errorf("cannot UpdateCrossUnsafe: %w: %s", types.ErrUnknownChain, chain) } db.logger.Debug("Updating cross unsafe", "chain", chain, "crossUnsafe", crossUnsafe) - db.crossUnsafe[chain] = crossUnsafe + v.Set(crossUnsafe) return nil } func (db *ChainsDB) UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, lastCrossDerived eth.BlockRef) error { - db.mu.RLock() - defer db.mu.RUnlock() - - crossDB, ok := db.crossDBs[chain] + crossDB, ok := db.crossDBs.Get(chain) if !ok { return fmt.Errorf("cannot UpdateCrossSafe: %w: %s", types.ErrUnknownChain, chain) } @@ -89,13 +72,14 @@ func (db *ChainsDB) UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, la } func (db *ChainsDB) UpdateFinalizedL1(finalized eth.BlockRef) error { - db.mu.RLock() - defer db.mu.RUnlock() + // Lock, so we avoid race-conditions in-between getting (for comparison) and setting. + db.finalizedL1.Lock() + defer db.finalizedL1.Unlock() - if db.finalizedL1.Number > finalized.Number { - return fmt.Errorf("cannot rewind finalized L1 head from %s to %s", db.finalizedL1, finalized) + if v := db.finalizedL1.Value; v.Number > finalized.Number { + return fmt.Errorf("cannot rewind finalized L1 head from %s to %s", v, finalized) } db.logger.Debug("Updating finalized L1", "finalizedL1", finalized) - db.finalizedL1 = finalized + db.finalizedL1.Value = finalized return nil } diff --git a/op-supervisor/supervisor/backend/mock.go b/op-supervisor/supervisor/backend/mock.go index c07e74a40013..b40c5209d5ef 100644 --- a/op-supervisor/supervisor/backend/mock.go +++ b/op-supervisor/supervisor/backend/mock.go @@ -62,7 +62,7 @@ func (m *MockBackend) Finalized(ctx context.Context, chainID types.ChainID) (eth return eth.BlockID{}, nil } -func (m *MockBackend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { +func (m *MockBackend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { return eth.BlockRef{}, nil } diff --git a/op-supervisor/supervisor/frontend/frontend.go b/op-supervisor/supervisor/frontend/frontend.go index 26e751200a5a..0a5b70a4799a 100644 --- a/op-supervisor/supervisor/frontend/frontend.go +++ b/op-supervisor/supervisor/frontend/frontend.go @@ -17,7 +17,7 @@ type AdminBackend interface { type QueryBackend interface { CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) CheckMessages(messages []types.Message, minSafety types.SafetyLevel) error - DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) + CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) UnsafeView(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) SafeView(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) @@ -67,8 +67,8 @@ func (q *QueryFrontend) Finalized(ctx context.Context, chainID types.ChainID) (e return q.Supervisor.Finalized(ctx, chainID) } -func (q *QueryFrontend) DerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { - return q.Supervisor.DerivedFrom(ctx, chainID, derived) +func (q *QueryFrontend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { + return q.Supervisor.CrossDerivedFrom(ctx, chainID, derived) } type AdminFrontend struct { diff --git a/op-supervisor/supervisor/service.go b/op-supervisor/supervisor/service.go index 469a80439e34..8bccaf1eb82f 100644 --- a/op-supervisor/supervisor/service.go +++ b/op-supervisor/supervisor/service.go @@ -72,6 +72,17 @@ func (su *SupervisorService) initBackend(ctx context.Context, cfg *config.Config su.backend = backend.NewMockBackend() return nil } + // the flag is a string slice, which has the potential to have empty strings + filterBlank := func(in []string) []string { + out := make([]string, 0, len(in)) + for _, s := range in { + if s != "" { + out = append(out, s) + } + } + return out + } + cfg.L2RPCs = filterBlank(cfg.L2RPCs) be, err := backend.NewSupervisorBackend(ctx, su.log, su.metrics, cfg) if err != nil { return fmt.Errorf("failed to create supervisor backend: %w", err) diff --git a/ops-bedrock/l2-op-geth-interop.Dockerfile b/ops-bedrock/l2-op-geth-interop.Dockerfile new file mode 100644 index 000000000000..41a667c0fc29 --- /dev/null +++ b/ops-bedrock/l2-op-geth-interop.Dockerfile @@ -0,0 +1,10 @@ +FROM us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101411.1-rc.3 +# Note: depend on dev-release for sequencer interop message checks + +RUN apk add --no-cache jq + +COPY l2-op-geth-entrypoint.sh /entrypoint.sh + +VOLUME ["/db"] + +ENTRYPOINT ["/bin/sh", "/entrypoint.sh"] diff --git a/ops/docker/op-stack-go/Dockerfile.dockerignore b/ops/docker/op-stack-go/Dockerfile.dockerignore index 1c0841df1f33..e5eceaea73b8 100644 --- a/ops/docker/op-stack-go/Dockerfile.dockerignore +++ b/ops/docker/op-stack-go/Dockerfile.dockerignore @@ -6,6 +6,7 @@ !/op-batcher !/op-bootnode !/op-chain-ops +!/op-deployer !/op-challenger !/packages/contracts-bedrock/snapshots !/op-dispute-mon From 1f335f15ac4dc94ca5bb147da5517c042efb44eb Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 31 Oct 2024 11:25:36 -0600 Subject: [PATCH 090/451] op-deployer: Add support for Isthmus calls to OPCM (#12753) --- op-chain-ops/interopgen/deploy.go | 2 +- .../pkg/deployer/opcm/deployOutput_v160.json | 77 +++++ op-deployer/pkg/deployer/opcm/opchain.go | 275 +++++++++--------- op-deployer/pkg/deployer/pipeline/opchain.go | 90 ++++-- op-deployer/pkg/deployer/standard/standard.go | 7 +- 5 files changed, 286 insertions(+), 165 deletions(-) create mode 100644 op-deployer/pkg/deployer/opcm/deployOutput_v160.json diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index fb1e71537d55..476406821ea5 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -200,7 +200,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) - output, err := opcm.DeployOPChain(l1Host, opcm.DeployOPChainInput{ + output, err := opcm.DeployOPChainV160(l1Host, opcm.DeployOPChainInputV160{ OpChainProxyAdminOwner: cfg.ProxyAdminOwner, SystemConfigOwner: cfg.SystemConfigOwner, Batcher: cfg.BatchSenderAddress, diff --git a/op-deployer/pkg/deployer/opcm/deployOutput_v160.json b/op-deployer/pkg/deployer/opcm/deployOutput_v160.json new file mode 100644 index 000000000000..888e28a102f8 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/deployOutput_v160.json @@ -0,0 +1,77 @@ +[ + { + "type": "function", + "name": "decodeOutput", + "inputs": [], + "outputs": [ + { + "name": "output", + "indexed": false, + "type": "tuple", + "components": [ + { + "name": "opChainProxyAdmin", + "type": "address" + }, + { + "name": "addressManager", + "type": "address" + }, + { + "name": "l1ERC721BridgeProxy", + "type": "address" + }, + { + "name": "systemConfigProxy", + "type": "address" + }, + { + "name": "optimismMintableERC20FactoryProxy", + "type": "address" + }, + { + "name": "l1StandardBridgeProxy", + "type": "address" + }, + { + "name": "l1CrossDomainMessengerProxy", + "type": "address" + }, + { + "name": "optimismPortalProxy", + "type": "address" + }, + { + "name": "disputeGameFactoryProxy", + "type": "address" + }, + { + "name": "anchorStateRegistryProxy", + "type": "address" + }, + { + "name": "anchorStateRegistryImpl", + "type": "address" + }, + { + "name": "faultDisputeGame", + "type": "address", + "internalType": "contract FaultDisputeGame" + }, + { + "name": "permissionedDisputeGame", + "type": "address" + }, + { + "name": "delayedWETHPermissionedGameProxy", + "type": "address" + }, + { + "name": "delayedWETHPermissionlessGameProxy", + "type": "address" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index b6bf4fcb6b7c..537765ce5154 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -6,6 +6,8 @@ import ( "math/big" "strings" + _ "embed" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" @@ -23,7 +25,49 @@ var PermissionedGameStartingAnchorRoots = []byte{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } -type DeployOPChainInput struct { +// opcmRolesBase is an internal struct used to pass the roles to OPCM. See opcmDeployInputV160 for more info. +type opcmRolesBase struct { + OpChainProxyAdminOwner common.Address + SystemConfigOwner common.Address + Batcher common.Address + UnsafeBlockSigner common.Address + Proposer common.Address + Challenger common.Address +} + +type opcmDeployInputBase struct { + BasefeeScalar uint32 + BlobBasefeeScalar uint32 + L2ChainId *big.Int + StartingAnchorRoots []byte + SaltMixer string + GasLimit uint64 + DisputeGameType uint32 + DisputeAbsolutePrestate common.Hash + DisputeMaxGameDepth *big.Int + DisputeSplitDepth *big.Int + DisputeClockExtension uint64 + DisputeMaxClockDuration uint64 +} + +// opcmDeployInputV160 is the input struct for the deploy method of the OPStackManager contract. We +// define a separate struct here to match what the OPSM contract expects. +type opcmDeployInputV160 struct { + opcmDeployInputBase + Roles opcmRolesBase +} + +type opcmRolesIsthmus struct { + opcmRolesBase + FeeAdmin common.Address +} + +type opcmDeployInputIsthmus struct { + opcmDeployInputBase + Roles opcmRolesIsthmus +} + +type DeployOPChainInputV160 struct { OpChainProxyAdminOwner common.Address SystemConfigOwner common.Address Batcher common.Address @@ -47,14 +91,57 @@ type DeployOPChainInput struct { AllowCustomDisputeParameters bool } -func (input *DeployOPChainInput) InputSet() bool { +func (input *DeployOPChainInputV160) InputSet() bool { return true } -func (input *DeployOPChainInput) StartingAnchorRoots() []byte { +func (input *DeployOPChainInputV160) StartingAnchorRoots() []byte { return PermissionedGameStartingAnchorRoots } +func DeployOPChainInputV160DeployCalldata(input DeployOPChainInputV160) any { + return opcmDeployInputV160{ + Roles: opcmRolesBase{ + OpChainProxyAdminOwner: input.OpChainProxyAdminOwner, + SystemConfigOwner: input.SystemConfigOwner, + Batcher: input.Batcher, + UnsafeBlockSigner: input.UnsafeBlockSigner, + Proposer: input.Proposer, + Challenger: input.Challenger, + }, + opcmDeployInputBase: opcmDeployInputBase{ + BasefeeScalar: input.BasefeeScalar, + BlobBasefeeScalar: input.BlobBaseFeeScalar, + L2ChainId: input.L2ChainId, + StartingAnchorRoots: input.StartingAnchorRoots(), + SaltMixer: input.SaltMixer, + GasLimit: input.GasLimit, + DisputeGameType: input.DisputeGameType, + DisputeAbsolutePrestate: input.DisputeAbsolutePrestate, + DisputeMaxGameDepth: new(big.Int).SetUint64(input.DisputeMaxGameDepth), + DisputeSplitDepth: new(big.Int).SetUint64(input.DisputeSplitDepth), + DisputeClockExtension: input.DisputeClockExtension, + DisputeMaxClockDuration: input.DisputeMaxClockDuration, + }, + } +} + +type DeployOPChainInputIsthmus struct { + DeployOPChainInputV160 + SystemConfigFeeAdmin common.Address +} + +func DeployOPChainInputIsthmusDeployCalldata(input DeployOPChainInputIsthmus) any { + v160Data := DeployOPChainInputV160DeployCalldata(input.DeployOPChainInputV160).(opcmDeployInputV160) + return opcmDeployInputIsthmus{ + Roles: opcmRolesIsthmus{ + opcmRolesBase: v160Data.Roles, + FeeAdmin: input.SystemConfigFeeAdmin, + }, + opcmDeployInputBase: v160Data.opcmDeployInputBase, + } +} + type DeployOPChainOutput struct { OpChainProxyAdmin common.Address AddressManager common.Address @@ -82,12 +169,20 @@ type DeployOPChainScript struct { Run func(input, output common.Address) error } -func DeployOPChain(host *script.Host, input DeployOPChainInput) (DeployOPChainOutput, error) { +func DeployOPChainV160(host *script.Host, input DeployOPChainInputV160) (DeployOPChainOutput, error) { + return deployOPChain(host, input) +} + +func DeployOPChainIsthmus(host *script.Host, input DeployOPChainInputIsthmus) (DeployOPChainOutput, error) { + return deployOPChain(host, input) +} + +func deployOPChain[T any](host *script.Host, input T) (DeployOPChainOutput, error) { var dco DeployOPChainOutput inputAddr := host.NewScriptAddress() outputAddr := host.NewScriptAddress() - cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](host, inputAddr, &input) + cleanupInput, err := script.WithPrecompileAtAddress[*T](host, inputAddr, &input) if err != nil { return dco, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err) } @@ -115,127 +210,46 @@ func DeployOPChain(host *script.Host, input DeployOPChainInput) (DeployOPChainOu return dco, nil } -// opcmRoles is an internal struct used to pass the roles to OPSM. See opcmDeployInput for more info. -type opcmRoles struct { - OpChainProxyAdminOwner common.Address - SystemConfigOwner common.Address - Batcher common.Address - UnsafeBlockSigner common.Address - Proposer common.Address - Challenger common.Address -} +// decodeOutputABIJSONV160 defines an ABI for a fake method called "decodeOutput" that returns the +// DeployOutput struct. This allows the code in the deployer to decode directly into a struct +// using Geth's ABI library. +// +//go:embed deployOutput_v160.json +var decodeOutputABIJSONV160 string -// opcmDeployInput is the input struct for the deploy method of the OPStackManager contract. We -// define a separate struct here to match what the OPSM contract expects. -type opcmDeployInput struct { - Roles opcmRoles - BasefeeScalar uint32 - BlobBasefeeScalar uint32 - L2ChainId *big.Int - StartingAnchorRoots []byte - SaltMixer string - GasLimit uint64 - DisputeGameType uint32 - DisputeAbsolutePrestate common.Hash - DisputeMaxGameDepth *big.Int - DisputeSplitDepth *big.Int - DisputeClockExtension uint64 - DisputeMaxClockDuration uint64 +var decodeOutputABIV160 abi.ABI + +func DeployOPChainRawV160( + ctx context.Context, + l1 *ethclient.Client, + bcast broadcaster.Broadcaster, + deployer common.Address, + artifacts foundry.StatDirFs, + input DeployOPChainInputV160, +) (DeployOPChainOutput, error) { + return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputV160DeployCalldata(input)) } -// decodeOutputABIJSON defines an ABI for a fake method called "decodeOutput" that returns the -// DeployOutput struct. This allows the code in the deployer to decode directly into a struct -// using Geth's ABI library. -const decodeOutputABIJSON = ` -[ - { - "type": "function", - "name": "decodeOutput", - "inputs": [], - "outputs": [ - { - "name": "output", - "indexed": false, - "type": "tuple", - "components": [ - { - "name": "opChainProxyAdmin", - "type": "address" - }, - { - "name": "addressManager", - "type": "address" - }, - { - "name": "l1ERC721BridgeProxy", - "type": "address" - }, - { - "name": "systemConfigProxy", - "type": "address" - }, - { - "name": "optimismMintableERC20FactoryProxy", - "type": "address" - }, - { - "name": "l1StandardBridgeProxy", - "type": "address" - }, - { - "name": "l1CrossDomainMessengerProxy", - "type": "address" - }, - { - "name": "optimismPortalProxy", - "type": "address" - }, - { - "name": "disputeGameFactoryProxy", - "type": "address" - }, - { - "name": "anchorStateRegistryProxy", - "type": "address" - }, - { - "name": "anchorStateRegistryImpl", - "type": "address" - }, - { - "name": "faultDisputeGame", - "type": "address", - "internalType": "contract FaultDisputeGame" - }, - { - "name": "permissionedDisputeGame", - "type": "address" - }, - { - "name": "delayedWETHPermissionedGameProxy", - "type": "address" - }, - { - "name": "delayedWETHPermissionlessGameProxy", - "type": "address" - } - ] - } - ] - } -] -` - -var decodeOutputABI abi.ABI +func DeployOPChainRawIsthmus( + ctx context.Context, + l1 *ethclient.Client, + bcast broadcaster.Broadcaster, + deployer common.Address, + artifacts foundry.StatDirFs, + input DeployOPChainInputIsthmus, +) (DeployOPChainOutput, error) { + return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputIsthmusDeployCalldata(input)) +} // DeployOPChainRaw deploys an OP Chain using a raw call to a pre-deployed OPSM contract. -func DeployOPChainRaw( +func deployOPChainRaw( ctx context.Context, l1 *ethclient.Client, bcast broadcaster.Broadcaster, deployer common.Address, artifacts foundry.StatDirFs, - input DeployOPChainInput, + opcmProxyAddress common.Address, + input any, ) (DeployOPChainOutput, error) { var out DeployOPChainOutput @@ -246,28 +260,7 @@ func DeployOPChainRaw( } opcmABI := opcmArtifacts.ABI - calldata, err := opcmABI.Pack("deploy", opcmDeployInput{ - Roles: opcmRoles{ - OpChainProxyAdminOwner: input.OpChainProxyAdminOwner, - SystemConfigOwner: input.SystemConfigOwner, - Batcher: input.Batcher, - UnsafeBlockSigner: input.UnsafeBlockSigner, - Proposer: input.Proposer, - Challenger: input.Challenger, - }, - BasefeeScalar: input.BasefeeScalar, - BlobBasefeeScalar: input.BlobBaseFeeScalar, - L2ChainId: input.L2ChainId, - StartingAnchorRoots: input.StartingAnchorRoots(), - SaltMixer: input.SaltMixer, - GasLimit: input.GasLimit, - DisputeGameType: input.DisputeGameType, - DisputeAbsolutePrestate: input.DisputeAbsolutePrestate, - DisputeMaxGameDepth: new(big.Int).SetUint64(input.DisputeMaxGameDepth), - DisputeSplitDepth: new(big.Int).SetUint64(input.DisputeSplitDepth), - DisputeClockExtension: input.DisputeClockExtension, - DisputeMaxClockDuration: input.DisputeMaxClockDuration, - }) + calldata, err := opcmABI.Pack("deploy", input) if err != nil { return out, fmt.Errorf("failed to pack deploy input: %w", err) } @@ -279,7 +272,7 @@ func DeployOPChainRaw( bcast.Hook(script.Broadcast{ From: deployer, - To: input.OpcmProxy, + To: opcmProxyAddress, Input: calldata, Value: (*hexutil.U256)(uint256.NewInt(0)), // use hardcoded 19MM gas for now since this is roughly what we've seen this deployment cost. @@ -313,7 +306,7 @@ func DeployOPChainRaw( Output DeployOPChainOutput } var outData OutputData - if err := decodeOutputABI.UnpackIntoInterface(&outData, "decodeOutput", data.DeployOutput); err != nil { + if err := decodeOutputABIV160.UnpackIntoInterface(&outData, "decodeOutput", data.DeployOutput); err != nil { return out, fmt.Errorf("failed to unpack DeployOutput: %w", err) } @@ -325,7 +318,7 @@ func DeployOPChainRaw( func init() { var err error - decodeOutputABI, err = abi.JSON(strings.NewReader(decodeOutputABIJSON)) + decodeOutputABIV160, err = abi.JSON(strings.NewReader(decodeOutputABIJSONV160)) if err != nil { panic(fmt.Sprintf("failed to parse decodeOutput ABI: %v", err)) } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index ebc6c1af0bc8..66699348709d 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -29,21 +29,43 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu return fmt.Errorf("failed to get chain intent: %w", err) } - input, err := makeDCI(intent, thisIntent, chainID, st) - if err != nil { - return fmt.Errorf("error making deploy OP chain input: %w", err) + var deployFunc func() (opcm.DeployOPChainOutput, error) + switch intent.L1ContractsLocator.Tag { + case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag: + deployFunc = func() (opcm.DeployOPChainOutput, error) { + input, err := makeDCIV160(intent, thisIntent, chainID, st) + if err != nil { + return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) + } + + return opcm.DeployOPChainRawV160(ctx, + env.L1Client, + env.Broadcaster, + env.Deployer, + bundle.L1, + input, + ) + } + default: + deployFunc = func() (opcm.DeployOPChainOutput, error) { + input, err := makeDCIIsthmus(intent, thisIntent, chainID, st) + if err != nil { + return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) + } + + return opcm.DeployOPChainRawIsthmus(ctx, + env.L1Client, + env.Broadcaster, + env.Deployer, + bundle.L1, + input, + ) + } } var dco opcm.DeployOPChainOutput lgr.Info("deploying OP chain using existing OPCM", "id", chainID.Hex(), "opcmAddress", st.ImplementationsDeployment.OpcmProxyAddress.Hex()) - dco, err = opcm.DeployOPChainRaw( - ctx, - env.L1Client, - env.Broadcaster, - env.Deployer, - bundle.L1, - input, - ) + dco, err = deployFunc() if err != nil { return fmt.Errorf("error deploying OP chain: %w", err) } @@ -158,19 +180,33 @@ func DeployOPChainGenesisStrategy(env *Env, intent *state.Intent, st *state.Stat return fmt.Errorf("failed to get chain intent: %w", err) } - input, err := makeDCI(intent, thisIntent, chainID, st) - if err != nil { - return fmt.Errorf("error making deploy OP chain input: %w", err) + var deployFunc func() (opcm.DeployOPChainOutput, error) + switch intent.L1ContractsLocator.Tag { + case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag: + deployFunc = func() (opcm.DeployOPChainOutput, error) { + input, err := makeDCIV160(intent, thisIntent, chainID, st) + if err != nil { + return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) + } + + return opcm.DeployOPChainV160(env.L1ScriptHost, input) + } + default: + deployFunc = func() (opcm.DeployOPChainOutput, error) { + input, err := makeDCIIsthmus(intent, thisIntent, chainID, st) + if err != nil { + return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) + } + + return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input) + } } env.L1ScriptHost.ImportState(st.L1StateDump.Data) var dco opcm.DeployOPChainOutput lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex()) - dco, err = opcm.DeployOPChain( - env.L1ScriptHost, - input, - ) + dco, err = deployFunc() if err != nil { return fmt.Errorf("error deploying OP chain: %w", err) } @@ -190,7 +226,7 @@ type ChainProofParams struct { DangerouslyAllowCustomDisputeParameters bool `json:"dangerouslyAllowCustomDisputeParameters" toml:"dangerouslyAllowCustomDisputeParameters"` } -func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInput, error) { +func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInputV160, error) { proofParams, err := jsonutil.MergeJSON( ChainProofParams{ DisputeGameType: standard.DisputeGameType, @@ -204,10 +240,10 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common thisIntent.DeployOverrides, ) if err != nil { - return opcm.DeployOPChainInput{}, fmt.Errorf("error merging proof params from overrides: %w", err) + return opcm.DeployOPChainInputV160{}, fmt.Errorf("error merging proof params from overrides: %w", err) } - return opcm.DeployOPChainInput{ + return opcm.DeployOPChainInputV160{ OpChainProxyAdminOwner: thisIntent.Roles.L1ProxyAdminOwner, SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, Batcher: thisIntent.Roles.Batcher, @@ -230,6 +266,18 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common }, nil } +func makeDCIIsthmus(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInputIsthmus, error) { + dci, err := makeDCIV160(intent, thisIntent, chainID, st) + if err != nil { + return opcm.DeployOPChainInputIsthmus{}, fmt.Errorf("error making deploy OP chain input: %w", err) + } + + return opcm.DeployOPChainInputIsthmus{ + DeployOPChainInputV160: dci, + SystemConfigFeeAdmin: common.Address{'D', 'E', 'A', 'D'}, + }, nil +} + func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.ChainState { return &state.ChainState{ ID: chainID, diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 2666891d96fc..46fb812b33c5 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -26,6 +26,9 @@ const ( DisputeSplitDepth uint64 = 30 DisputeClockExtension uint64 = 10800 DisputeMaxClockDuration uint64 = 302400 + + ContractsV160Tag = "op-contracts/v1.6.0" + ContractsV170Beta1L2Tag = "op-contracts/v1.7.0-beta.1+l2-contracts" ) var DisputeAbsolutePrestate = common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -40,9 +43,9 @@ var L1VersionsSepolia L1Versions var L1VersionsMainnet L1Versions -var DefaultL1ContractsTag = "op-contracts/v1.6.0" +var DefaultL1ContractsTag = ContractsV160Tag -var DefaultL2ContractsTag = "op-contracts/v1.7.0-beta.1+l2-contracts" +var DefaultL2ContractsTag = ContractsV170Beta1L2Tag type L1Versions struct { Releases map[string]L1VersionsReleases `toml:"releases"` From 2c5f6ed312aa7b92e9c23d3c7056bed7735f8f47 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 1 Nov 2024 02:57:10 +0700 Subject: [PATCH 091/451] feat(ct): DeployDelayedWETH script (#12662) * feat(ct): DeployDelayedWETH script Adds a new deployment script for deploying a new DelayedWETH proxy contract. * fix tests * lint * delete letter o * move testdata --------- Co-authored-by: Matthew Slipper --- op-deployer/pkg/deployer/apply.go | 10 +- .../{pipeline => artifacts}/downloader.go | 6 +- .../downloader_test.go | 8 +- .../locator.go} | 24 +- .../locator_test.go} | 14 +- .../testdata/artifacts.tar.gz | Bin .../pkg/deployer/bootstrap/delayed_weth.go | 204 +++++++++++++++ op-deployer/pkg/deployer/bootstrap/flags.go | 12 + op-deployer/pkg/deployer/bootstrap/opcm.go | 12 +- op-deployer/pkg/deployer/init.go | 6 +- op-deployer/pkg/deployer/inspect/semvers.go | 8 +- .../deployer/integration_test/apply_test.go | 68 ++--- op-deployer/pkg/deployer/opcm/delayed_weth.go | 71 +++++ .../pkg/deployer/opcm/delayed_weth_test.go | 46 ++++ .../pkg/deployer/pipeline/l2genesis.go | 4 +- op-deployer/pkg/deployer/pipeline/opchain.go | 4 +- op-deployer/pkg/deployer/standard/standard.go | 13 + op-deployer/pkg/deployer/state/intent.go | 12 +- op-deployer/pkg/deployer/testutil/env.go | 37 +++ .../pkg/{deployer/pipeline => env}/host.go | 2 +- .../scripts/deploy/DeployDelayedWETH.s.sol | 245 ++++++++++++++++++ 21 files changed, 708 insertions(+), 98 deletions(-) rename op-deployer/pkg/deployer/{pipeline => artifacts}/downloader.go (94%) rename op-deployer/pkg/deployer/{pipeline => artifacts}/downloader_test.go (82%) rename op-deployer/pkg/deployer/{opcm/artifacts_locator.go => artifacts/locator.go} (66%) rename op-deployer/pkg/deployer/{opcm/artifacts_locator_test.go => artifacts/locator_test.go} (88%) rename op-deployer/pkg/deployer/{pipeline => artifacts}/testdata/artifacts.tar.gz (100%) create mode 100644 op-deployer/pkg/deployer/bootstrap/delayed_weth.go create mode 100644 op-deployer/pkg/deployer/opcm/delayed_weth.go create mode 100644 op-deployer/pkg/deployer/opcm/delayed_weth_test.go create mode 100644 op-deployer/pkg/deployer/testutil/env.go rename op-deployer/pkg/{deployer/pipeline => env}/host.go (98%) create mode 100644 packages/contracts-bedrock/scripts/deploy/DeployDelayedWETH.s.sol diff --git a/op-deployer/pkg/deployer/apply.go b/op-deployer/pkg/deployer/apply.go index 95db427d8efb..f6eb38d11e08 100644 --- a/op-deployer/pkg/deployer/apply.go +++ b/op-deployer/pkg/deployer/apply.go @@ -7,6 +7,10 @@ import ( "math/big" "strings" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum/go-ethereum/common" @@ -146,7 +150,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { cfg.Logger.Info("artifacts download progress", "current", curr, "total", total) } - l1ArtifactsFS, cleanupL1, err := pipeline.DownloadArtifacts(ctx, intent.L1ContractsLocator, progressor) + l1ArtifactsFS, cleanupL1, err := artifacts.Download(ctx, intent.L1ContractsLocator, progressor) if err != nil { return fmt.Errorf("failed to download L1 artifacts: %w", err) } @@ -156,7 +160,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { } }() - l2ArtifactsFS, cleanupL2, err := pipeline.DownloadArtifacts(ctx, intent.L2ContractsLocator, progressor) + l2ArtifactsFS, cleanupL2, err := artifacts.Download(ctx, intent.L2ContractsLocator, progressor) if err != nil { return fmt.Errorf("failed to download L2 artifacts: %w", err) } @@ -171,7 +175,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { L2: l2ArtifactsFS, } - l1Host, err := pipeline.DefaultScriptHost(bcaster, cfg.Logger, deployer, bundle.L1, startingNonce) + l1Host, err := env.DefaultScriptHost(bcaster, cfg.Logger, deployer, bundle.L1, startingNonce) if err != nil { return fmt.Errorf("failed to create L1 script host: %w", err) } diff --git a/op-deployer/pkg/deployer/pipeline/downloader.go b/op-deployer/pkg/deployer/artifacts/downloader.go similarity index 94% rename from op-deployer/pkg/deployer/pipeline/downloader.go rename to op-deployer/pkg/deployer/artifacts/downloader.go index ffafd6096869..1303adbe86aa 100644 --- a/op-deployer/pkg/deployer/pipeline/downloader.go +++ b/op-deployer/pkg/deployer/artifacts/downloader.go @@ -1,4 +1,4 @@ -package pipeline +package artifacts import ( "archive/tar" @@ -19,8 +19,6 @@ import ( "github.com/ethereum/go-ethereum/log" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" - "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" ) @@ -40,7 +38,7 @@ func LogProgressor(lgr log.Logger) DownloadProgressor { } } -func DownloadArtifacts(ctx context.Context, loc *opcm.ArtifactsLocator, progress DownloadProgressor) (foundry.StatDirFs, CleanupFunc, error) { +func Download(ctx context.Context, loc *Locator, progress DownloadProgressor) (foundry.StatDirFs, CleanupFunc, error) { var u *url.URL var err error if loc.IsTag() { diff --git a/op-deployer/pkg/deployer/pipeline/downloader_test.go b/op-deployer/pkg/deployer/artifacts/downloader_test.go similarity index 82% rename from op-deployer/pkg/deployer/pipeline/downloader_test.go rename to op-deployer/pkg/deployer/artifacts/downloader_test.go index 36183fe5afa5..e66b41f96a81 100644 --- a/op-deployer/pkg/deployer/pipeline/downloader_test.go +++ b/op-deployer/pkg/deployer/artifacts/downloader_test.go @@ -1,4 +1,4 @@ -package pipeline +package artifacts import ( "context" @@ -9,8 +9,6 @@ import ( "os" "testing" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" - "github.com/stretchr/testify/require" ) @@ -29,11 +27,11 @@ func TestDownloadArtifacts(t *testing.T) { ctx := context.Background() artifactsURL, err := url.Parse(ts.URL) require.NoError(t, err) - loc := &opcm.ArtifactsLocator{ + loc := &Locator{ URL: artifactsURL, } - fs, cleanup, err := DownloadArtifacts(ctx, loc, nil) + fs, cleanup, err := Download(ctx, loc, nil) require.NoError(t, err) require.NotNil(t, fs) defer func() { diff --git a/op-deployer/pkg/deployer/opcm/artifacts_locator.go b/op-deployer/pkg/deployer/artifacts/locator.go similarity index 66% rename from op-deployer/pkg/deployer/opcm/artifacts_locator.go rename to op-deployer/pkg/deployer/artifacts/locator.go index 02a2b60b59fb..160e8790420b 100644 --- a/op-deployer/pkg/deployer/opcm/artifacts_locator.go +++ b/op-deployer/pkg/deployer/artifacts/locator.go @@ -1,4 +1,4 @@ -package opcm +package artifacts import ( "fmt" @@ -8,7 +8,7 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" ) -type schemeUnmarshaler func(string) (*ArtifactsLocator, error) +type schemeUnmarshaler func(string) (*Locator, error) var schemeUnmarshalerDispatch = map[string]schemeUnmarshaler{ "tag": unmarshalTag, @@ -16,20 +16,20 @@ var schemeUnmarshalerDispatch = map[string]schemeUnmarshaler{ "https": unmarshalURL, } -var DefaultL1ContractsLocator = &ArtifactsLocator{ +var DefaultL1ContractsLocator = &Locator{ Tag: standard.DefaultL1ContractsTag, } -var DefaultL2ContractsLocator = &ArtifactsLocator{ +var DefaultL2ContractsLocator = &Locator{ Tag: standard.DefaultL2ContractsTag, } -type ArtifactsLocator struct { +type Locator struct { URL *url.URL Tag string } -func (a *ArtifactsLocator) UnmarshalText(text []byte) error { +func (a *Locator) UnmarshalText(text []byte) error { str := string(text) for scheme, unmarshaler := range schemeUnmarshalerDispatch { @@ -49,7 +49,7 @@ func (a *ArtifactsLocator) UnmarshalText(text []byte) error { return fmt.Errorf("unsupported scheme") } -func (a *ArtifactsLocator) MarshalText() ([]byte, error) { +func (a *Locator) MarshalText() ([]byte, error) { if a.URL != nil { return []byte(a.URL.String()), nil } @@ -61,11 +61,11 @@ func (a *ArtifactsLocator) MarshalText() ([]byte, error) { return nil, fmt.Errorf("no URL, path or tag set") } -func (a *ArtifactsLocator) IsTag() bool { +func (a *Locator) IsTag() bool { return a.Tag != "" } -func unmarshalTag(tag string) (*ArtifactsLocator, error) { +func unmarshalTag(tag string) (*Locator, error) { tag = strings.TrimPrefix(tag, "tag://") if !strings.HasPrefix(tag, "op-contracts/") { return nil, fmt.Errorf("invalid tag: %s", tag) @@ -75,14 +75,14 @@ func unmarshalTag(tag string) (*ArtifactsLocator, error) { return nil, err } - return &ArtifactsLocator{Tag: tag}, nil + return &Locator{Tag: tag}, nil } -func unmarshalURL(text string) (*ArtifactsLocator, error) { +func unmarshalURL(text string) (*Locator, error) { u, err := url.Parse(text) if err != nil { return nil, err } - return &ArtifactsLocator{URL: u}, nil + return &Locator{URL: u}, nil } diff --git a/op-deployer/pkg/deployer/opcm/artifacts_locator_test.go b/op-deployer/pkg/deployer/artifacts/locator_test.go similarity index 88% rename from op-deployer/pkg/deployer/opcm/artifacts_locator_test.go rename to op-deployer/pkg/deployer/artifacts/locator_test.go index 8771fad32ec9..678e2c252b32 100644 --- a/op-deployer/pkg/deployer/opcm/artifacts_locator_test.go +++ b/op-deployer/pkg/deployer/artifacts/locator_test.go @@ -1,4 +1,4 @@ -package opcm +package artifacts import ( "net/url" @@ -7,17 +7,17 @@ import ( "github.com/stretchr/testify/require" ) -func TestArtifactsLocator_Marshaling(t *testing.T) { +func TestLocator_Marshaling(t *testing.T) { tests := []struct { name string in string - out *ArtifactsLocator + out *Locator err bool }{ { name: "valid tag", in: "tag://op-contracts/v1.6.0", - out: &ArtifactsLocator{ + out: &Locator{ Tag: "op-contracts/v1.6.0", }, err: false, @@ -37,7 +37,7 @@ func TestArtifactsLocator_Marshaling(t *testing.T) { { name: "valid HTTPS URL", in: "https://example.com", - out: &ArtifactsLocator{ + out: &Locator{ URL: parseUrl(t, "https://example.com"), }, err: false, @@ -45,7 +45,7 @@ func TestArtifactsLocator_Marshaling(t *testing.T) { { name: "valid file URL", in: "file:///tmp/artifacts", - out: &ArtifactsLocator{ + out: &Locator{ URL: parseUrl(t, "file:///tmp/artifacts"), }, err: false, @@ -71,7 +71,7 @@ func TestArtifactsLocator_Marshaling(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var a ArtifactsLocator + var a Locator err := a.UnmarshalText([]byte(tt.in)) if tt.err { require.Error(t, err) diff --git a/op-deployer/pkg/deployer/pipeline/testdata/artifacts.tar.gz b/op-deployer/pkg/deployer/artifacts/testdata/artifacts.tar.gz similarity index 100% rename from op-deployer/pkg/deployer/pipeline/testdata/artifacts.tar.gz rename to op-deployer/pkg/deployer/artifacts/testdata/artifacts.tar.gz diff --git a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go new file mode 100644 index 000000000000..45952d19e955 --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go @@ -0,0 +1,204 @@ +package bootstrap + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "strings" + + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" +) + +type DelayedWETHConfig struct { + L1RPCUrl string + PrivateKey string + Logger log.Logger + ArtifactsLocator *artifacts2.Locator + + privateKeyECDSA *ecdsa.PrivateKey +} + +func (c *DelayedWETHConfig) Check() error { + if c.L1RPCUrl == "" { + return fmt.Errorf("l1RPCUrl must be specified") + } + + if c.PrivateKey == "" { + return fmt.Errorf("private key must be specified") + } + + privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x")) + if err != nil { + return fmt.Errorf("failed to parse private key: %w", err) + } + c.privateKeyECDSA = privECDSA + + if c.Logger == nil { + return fmt.Errorf("logger must be specified") + } + + if c.ArtifactsLocator == nil { + return fmt.Errorf("artifacts locator must be specified") + } + + return nil +} + +func DelayedWETHCLI(cliCtx *cli.Context) error { + logCfg := oplog.ReadCLIConfig(cliCtx) + l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) + oplog.SetGlobalLogHandler(l.Handler()) + + l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) + privateKey := cliCtx.String(deployer.PrivateKeyFlagName) + artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) + artifactsLocator := new(artifacts2.Locator) + if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { + return fmt.Errorf("failed to parse artifacts URL: %w", err) + } + + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + + return DelayedWETH(ctx, DelayedWETHConfig{ + L1RPCUrl: l1RPCUrl, + PrivateKey: privateKey, + Logger: l, + ArtifactsLocator: artifactsLocator, + }) +} + +func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error { + if err := cfg.Check(); err != nil { + return fmt.Errorf("invalid config for DelayedWETH: %w", err) + } + + lgr := cfg.Logger + progressor := func(curr, total int64) { + lgr.Info("artifacts download progress", "current", curr, "total", total) + } + + artifactsFS, cleanup, err := artifacts2.Download(ctx, cfg.ArtifactsLocator, progressor) + if err != nil { + return fmt.Errorf("failed to download artifacts: %w", err) + } + defer func() { + if err := cleanup(); err != nil { + lgr.Warn("failed to clean up artifacts", "err", err) + } + }() + + l1Client, err := ethclient.Dial(cfg.L1RPCUrl) + if err != nil { + return fmt.Errorf("failed to connect to L1 RPC: %w", err) + } + + chainID, err := l1Client.ChainID(ctx) + if err != nil { + return fmt.Errorf("failed to get chain ID: %w", err) + } + chainIDU64 := chainID.Uint64() + + superCfg, err := standard.SuperchainFor(chainIDU64) + if err != nil { + return fmt.Errorf("error getting superchain config: %w", err) + } + standardVersionsTOML, err := standard.L1VersionsDataFor(chainIDU64) + if err != nil { + return fmt.Errorf("error getting standard versions TOML: %w", err) + } + proxyAdmin, err := standard.ManagerOwnerAddrFor(chainIDU64) + if err != nil { + return fmt.Errorf("error getting superchain proxy admin: %w", err) + } + delayedWethOwner, err := standard.SystemOwnerAddrFor(chainIDU64) + if err != nil { + return fmt.Errorf("error getting superchain system owner: %w", err) + } + + signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) + chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) + + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: lgr, + ChainID: chainID, + Client: l1Client, + Signer: signer, + From: chainDeployer, + }) + if err != nil { + return fmt.Errorf("failed to create broadcaster: %w", err) + } + + nonce, err := l1Client.NonceAt(ctx, chainDeployer, nil) + if err != nil { + return fmt.Errorf("failed to get starting nonce: %w", err) + } + + host, err := env.DefaultScriptHost( + bcaster, + lgr, + chainDeployer, + artifactsFS, + nonce, + ) + if err != nil { + return fmt.Errorf("failed to create script host: %w", err) + } + + var release string + if cfg.ArtifactsLocator.IsTag() { + release = cfg.ArtifactsLocator.Tag + } else { + release = "dev" + } + + lgr.Info("deploying DelayedWETH", "release", release) + + superchainConfigAddr := common.Address(*superCfg.Config.SuperchainConfigAddr) + + dwo, err := opcm.DeployDelayedWETH( + host, + opcm.DeployDelayedWETHInput{ + Release: release, + StandardVersionsToml: standardVersionsTOML, + ProxyAdmin: proxyAdmin, + SuperchainConfigProxy: superchainConfigAddr, + DelayedWethOwner: delayedWethOwner, + DelayedWethDelay: big.NewInt(604800), + }, + ) + if err != nil { + return fmt.Errorf("error deploying implementations: %w", err) + } + + if _, err := bcaster.Broadcast(ctx); err != nil { + return fmt.Errorf("failed to broadcast: %w", err) + } + + lgr.Info("deployed DelayedWETH") + + if err := jsonutil.WriteJSON(dwo, ioutil.ToStdOut()); err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + return nil +} diff --git a/op-deployer/pkg/deployer/bootstrap/flags.go b/op-deployer/pkg/deployer/bootstrap/flags.go index d0c9eb5e2ce2..6fd598780df5 100644 --- a/op-deployer/pkg/deployer/bootstrap/flags.go +++ b/op-deployer/pkg/deployer/bootstrap/flags.go @@ -73,6 +73,12 @@ var OPCMFlags = []cli.Flag{ MIPSVersionFlag, } +var DelayedWETHFlags = []cli.Flag{ + deployer.L1RPCURLFlag, + deployer.PrivateKeyFlag, + ArtifactsLocatorFlag, +} + var Commands = []*cli.Command{ { Name: "opcm", @@ -80,4 +86,10 @@ var Commands = []*cli.Command{ Flags: cliapp.ProtectFlags(OPCMFlags), Action: OPCMCLI, }, + { + Name: "delayedweth", + Usage: "Bootstrap an instance of DelayedWETH.", + Flags: cliapp.ProtectFlags(DelayedWETHFlags), + Action: DelayedWETHCLI, + }, } diff --git a/op-deployer/pkg/deployer/bootstrap/opcm.go b/op-deployer/pkg/deployer/bootstrap/opcm.go index 4b792ef92160..d584d6975f41 100644 --- a/op-deployer/pkg/deployer/bootstrap/opcm.go +++ b/op-deployer/pkg/deployer/bootstrap/opcm.go @@ -8,6 +8,10 @@ import ( "math/big" "strings" + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" @@ -35,7 +39,7 @@ type OPCMConfig struct { L1RPCUrl string PrivateKey string Logger log.Logger - ArtifactsLocator *opcm.ArtifactsLocator + ArtifactsLocator *artifacts2.Locator privateKeyECDSA *ecdsa.PrivateKey } @@ -98,7 +102,7 @@ func OPCMCLI(cliCtx *cli.Context) error { l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) privateKey := cliCtx.String(deployer.PrivateKeyFlagName) artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) - artifactsLocator := new(opcm.ArtifactsLocator) + artifactsLocator := new(artifacts2.Locator) if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { return fmt.Errorf("failed to parse artifacts URL: %w", err) } @@ -131,7 +135,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { lgr.Info("artifacts download progress", "current", curr, "total", total) } - artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, cfg.ArtifactsLocator, progressor) + artifactsFS, cleanup, err := artifacts2.Download(ctx, cfg.ArtifactsLocator, progressor) if err != nil { return fmt.Errorf("failed to download artifacts: %w", err) } @@ -184,7 +188,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { return fmt.Errorf("failed to get starting nonce: %w", err) } - host, err := pipeline.DefaultScriptHost( + host, err := env.DefaultScriptHost( bcaster, lgr, chainDeployer, diff --git a/op-deployer/pkg/deployer/init.go b/op-deployer/pkg/deployer/init.go index 6dd26222fb82..3a8ae27d2ae7 100644 --- a/op-deployer/pkg/deployer/init.go +++ b/op-deployer/pkg/deployer/init.go @@ -7,7 +7,7 @@ import ( "path" "strings" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" @@ -90,8 +90,8 @@ func Init(cfg InitConfig) error { DeploymentStrategy: cfg.DeploymentStrategy, L1ChainID: cfg.L1ChainID, FundDevAccounts: true, - L1ContractsLocator: opcm.DefaultL1ContractsLocator, - L2ContractsLocator: opcm.DefaultL2ContractsLocator, + L1ContractsLocator: artifacts.DefaultL1ContractsLocator, + L2ContractsLocator: artifacts.DefaultL2ContractsLocator, } l1ChainIDBig := intent.L1ChainIDBig() diff --git a/op-deployer/pkg/deployer/inspect/semvers.go b/op-deployer/pkg/deployer/inspect/semvers.go index f88f2c01476a..7c8e4ce9f3dd 100644 --- a/op-deployer/pkg/deployer/inspect/semvers.go +++ b/op-deployer/pkg/deployer/inspect/semvers.go @@ -8,6 +8,10 @@ import ( "regexp" "time" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" @@ -53,7 +57,7 @@ func L2SemversCLI(cliCtx *cli.Context) error { return fmt.Errorf("chain state does not have allocs") } - artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, intent.L2ContractsLocator, pipeline.LogProgressor(l)) + artifactsFS, cleanup, err := artifacts.Download(ctx, intent.L2ContractsLocator, artifacts.LogProgressor(l)) if err != nil { return fmt.Errorf("failed to download L2 artifacts: %w", err) } @@ -63,7 +67,7 @@ func L2SemversCLI(cliCtx *cli.Context) error { } }() - host, err := pipeline.DefaultScriptHost( + host, err := env.DefaultScriptHost( broadcaster.NoopBroadcaster(), l, common.Address{19: 0x01}, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index af240aeffa73..e0b34dc8ea6a 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -7,14 +7,16 @@ import ( "fmt" "log/slog" "math/big" - "net/url" "os" - "path" - "runtime" "testing" "time" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/testutils/anvil" @@ -22,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" @@ -109,7 +110,7 @@ func TestEndToEndApply(t *testing.T) { deployerAddr, err := dk.Address(depKey) require.NoError(t, err) - loc := localArtifactsLocator(t) + loc, _ := testutil.LocalArtifacts(t) bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ Logger: log.NewLogger(log.DiscardHandler()), @@ -120,7 +121,7 @@ func TestEndToEndApply(t *testing.T) { }) require.NoError(t, err) - env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) + env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) cg := ethClientCodeGetter(ctx, l1Client) @@ -140,7 +141,7 @@ func TestEndToEndApply(t *testing.T) { t.Run("subsequent chain", func(t *testing.T) { // create a new environment with wiped state to ensure we can continue using the // state from the previous deployment - env, bundle, _ = createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) + env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2)) require.NoError(t, deployer.ApplyPipeline( @@ -202,15 +203,15 @@ func TestApplyExistingOPCM(t *testing.T) { }) require.NoError(t, err) - env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr) + env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent, st := newIntent( t, l1ChainID, dk, l2ChainID, - opcm.DefaultL1ContractsLocator, - opcm.DefaultL2ContractsLocator, + artifacts.DefaultL1ContractsLocator, + artifacts.DefaultL2ContractsLocator, ) require.NoError(t, deployer.ApplyPipeline( @@ -421,7 +422,7 @@ func TestInvalidL2Genesis(t *testing.T) { deployerAddr, err := dk.Address(depKey) require.NoError(t, err) - loc := localArtifactsLocator(t) + loc, _ := testutil.LocalArtifacts(t) // these tests were generated by grepping all usages of the deploy // config in L2Genesis.s.sol. @@ -468,7 +469,7 @@ func TestInvalidL2Genesis(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis @@ -490,9 +491,6 @@ func TestInvalidL2Genesis(t *testing.T) { func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *state.Intent, *state.State) { lgr := testlog.Logger(t, slog.LevelDebug) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - depKey := new(deployerKey) l1ChainID := big.NewInt(77799777) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) @@ -503,53 +501,25 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, * deployerAddr, err := dk.Address(depKey) require.NoError(t, err) - loc := localArtifactsLocator(t) + loc, _ := testutil.LocalArtifacts(t) - env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis return env, bundle, intent, st } -func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator { - _, testFilename, _, ok := runtime.Caller(0) - require.Truef(t, ok, "failed to get test filename") - monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") - artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) - require.NoError(t, err) - loc := &opcm.ArtifactsLocator{ - URL: artifactsURL, - } - return loc -} - func createEnv( t *testing.T, - ctx context.Context, lgr log.Logger, l1Client *ethclient.Client, bcaster broadcaster.Broadcaster, deployerAddr common.Address, ) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) { - _, testFilename, _, ok := runtime.Caller(0) - require.Truef(t, ok, "failed to get test filename") - monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..") - artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) - require.NoError(t, err) - artifactsLocator := &opcm.ArtifactsLocator{ - URL: artifactsURL, - } - - artifactsFS, cleanupArtifacts, err := pipeline.DownloadArtifacts(ctx, artifactsLocator, pipeline.NoopDownloadProgressor) - require.NoError(t, err) - defer func() { - require.NoError(t, cleanupArtifacts()) - }() + _, artifactsFS := testutil.LocalArtifacts(t) - host, err := pipeline.DefaultScriptHost( + host, err := env.DefaultScriptHost( bcaster, lgr, deployerAddr, @@ -586,8 +556,8 @@ func newIntent( l1ChainID *big.Int, dk *devkeys.MnemonicDevKeys, l2ChainID *uint256.Int, - l1Loc *opcm.ArtifactsLocator, - l2Loc *opcm.ArtifactsLocator, + l1Loc *artifacts.Locator, + l2Loc *artifacts.Locator, ) (*state.Intent, *state.State) { intent := &state.Intent{ DeploymentStrategy: state.DeploymentStrategyLive, diff --git a/op-deployer/pkg/deployer/opcm/delayed_weth.go b/op-deployer/pkg/deployer/opcm/delayed_weth.go new file mode 100644 index 000000000000..d94e25cb9092 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/delayed_weth.go @@ -0,0 +1,71 @@ +package opcm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +type DeployDelayedWETHInput struct { + Release string + StandardVersionsToml string + ProxyAdmin common.Address + SuperchainConfigProxy common.Address + DelayedWethOwner common.Address + DelayedWethDelay *big.Int +} + +func (input *DeployDelayedWETHInput) InputSet() bool { + return true +} + +type DeployDelayedWETHOutput struct { + DelayedWethImpl common.Address + DelayedWethProxy common.Address +} + +func (output *DeployDelayedWETHOutput) CheckOutput(input common.Address) error { + return nil +} + +type DeployDelayedWETHScript struct { + Run func(input, output common.Address) error +} + +func DeployDelayedWETH( + host *script.Host, + input DeployDelayedWETHInput, +) (DeployDelayedWETHOutput, error) { + var output DeployDelayedWETHOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress[*DeployDelayedWETHInput](host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployDelayedWETHInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployDelayedWETHOutput](host, outputAddr, &output, + script.WithFieldSetter[*DeployDelayedWETHOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployDelayedWETHOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployDelayedWETH" + deployScript, cleanupDeploy, err := script.WithScript[DeployDelayedWETHScript](host, "DeployDelayedWETH.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/opcm/delayed_weth_test.go b/op-deployer/pkg/deployer/opcm/delayed_weth_test.go new file mode 100644 index 000000000000..98ca7ab6996b --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/delayed_weth_test.go @@ -0,0 +1,46 @@ +package opcm + +import ( + "math/big" + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestDeployDelayedWETH(t *testing.T) { + _, artifacts := testutil.LocalArtifacts(t) + + host, err := env.DefaultScriptHost( + broadcaster.NoopBroadcaster(), + testlog.Logger(t, log.LevelInfo), + common.Address{'D'}, + artifacts, + 0, + ) + require.NoError(t, err) + + standardVersionsTOML, err := standard.L1VersionsDataFor(11155111) + require.NoError(t, err) + + input := DeployDelayedWETHInput{ + Release: "dev", + StandardVersionsToml: standardVersionsTOML, + ProxyAdmin: common.Address{'P'}, + SuperchainConfigProxy: common.Address{'S'}, + DelayedWethOwner: common.Address{'O'}, + DelayedWethDelay: big.NewInt(100), + } + + output, err := DeployDelayedWETH(host, input) + require.NoError(t, err) + + require.NotEmpty(t, output.DelayedWethImpl) + require.NotEmpty(t, output.DelayedWethProxy) +} diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis.go b/op-deployer/pkg/deployer/pipeline/l2genesis.go index 4fa8755df6ee..a5dabefd8e84 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis.go @@ -3,6 +3,8 @@ package pipeline import ( "fmt" + env2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" @@ -36,7 +38,7 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s return fmt.Errorf("failed to combine L2 init config: %w", err) } - host, err := DefaultScriptHost( + host, err := env2.DefaultScriptHost( broadcaster.NoopBroadcaster(), env.Logger, env.Deployer, diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 66699348709d..59e5d214cf4f 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/jsonutil" @@ -144,7 +146,7 @@ func conditionallySetImplementationAddresses(ctx context.Context, client *ethcli return nil } -func setMipsSingletonAddress(ctx context.Context, client *ethclient.Client, l1ArtifactsLocator *opcm.ArtifactsLocator, errCh chan error, opcmProxyAddress common.Address, singletonAddress *common.Address) { +func setMipsSingletonAddress(ctx context.Context, client *ethclient.Client, l1ArtifactsLocator *artifacts.Locator, errCh chan error, opcmProxyAddress common.Address, singletonAddress *common.Address) { if !l1ArtifactsLocator.IsTag() { errCh <- errors.New("L1 contracts locator is not a tag, cannot set MIPS singleton address") return diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 46fb812b33c5..78144ca1a7bf 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -156,6 +156,19 @@ func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) { } } +func SystemOwnerAddrFor(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + // Set to owner of superchain proxy admin + return common.HexToAddress("0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A"), nil + case 11155111: + // Set to development multisig + return common.HexToAddress("0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + func ArtifactsURLForTag(tag string) (*url.URL, error) { switch tag { case "op-contracts/v1.6.0": diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index cbabe37fc8ef..681d166bc142 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -4,9 +4,9 @@ import ( "fmt" "math/big" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil" @@ -42,9 +42,9 @@ type Intent struct { UseInterop bool `json:"useInterop" toml:"useInterop"` - L1ContractsLocator *opcm.ArtifactsLocator `json:"l1ContractsLocator" toml:"l1ContractsLocator"` + L1ContractsLocator *artifacts.Locator `json:"l1ContractsLocator" toml:"l1ContractsLocator"` - L2ContractsLocator *opcm.ArtifactsLocator `json:"l2ContractsLocator" toml:"l2ContractsLocator"` + L2ContractsLocator *artifacts.Locator `json:"l2ContractsLocator" toml:"l2ContractsLocator"` Chains []*ChainIntent `json:"chains" toml:"chains"` @@ -65,11 +65,11 @@ func (c *Intent) Check() error { } if c.L1ContractsLocator == nil { - c.L1ContractsLocator = opcm.DefaultL1ContractsLocator + c.L1ContractsLocator = artifacts.DefaultL1ContractsLocator } if c.L2ContractsLocator == nil { - c.L2ContractsLocator = opcm.DefaultL2ContractsLocator + c.L2ContractsLocator = artifacts.DefaultL2ContractsLocator } var err error diff --git a/op-deployer/pkg/deployer/testutil/env.go b/op-deployer/pkg/deployer/testutil/env.go new file mode 100644 index 000000000000..6b289c4503b7 --- /dev/null +++ b/op-deployer/pkg/deployer/testutil/env.go @@ -0,0 +1,37 @@ +package testutil + +import ( + "context" + "fmt" + "net/url" + "path" + "runtime" + "testing" + + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + op_service "github.com/ethereum-optimism/optimism/op-service" + "github.com/stretchr/testify/require" +) + +func LocalArtifacts(t *testing.T) (*artifacts2.Locator, foundry.StatDirFs) { + _, testFilename, _, ok := runtime.Caller(0) + require.Truef(t, ok, "failed to get test filename") + monorepoDir, err := op_service.FindMonorepoRoot(testFilename) + require.NoError(t, err) + artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") + artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) + require.NoError(t, err) + loc := &artifacts2.Locator{ + URL: artifactsURL, + } + + artifactsFS, cleanupArtifacts, err := artifacts2.Download(context.Background(), loc, artifacts2.NoopDownloadProgressor) + require.NoError(t, err) + t.Cleanup(func() { + _ = cleanupArtifacts() + }) + + return loc, artifactsFS +} diff --git a/op-deployer/pkg/deployer/pipeline/host.go b/op-deployer/pkg/env/host.go similarity index 98% rename from op-deployer/pkg/deployer/pipeline/host.go rename to op-deployer/pkg/env/host.go index df8e6c9c789b..04a0c4a172d6 100644 --- a/op-deployer/pkg/deployer/pipeline/host.go +++ b/op-deployer/pkg/env/host.go @@ -1,4 +1,4 @@ -package pipeline +package env import ( "fmt" diff --git a/packages/contracts-bedrock/scripts/deploy/DeployDelayedWETH.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployDelayedWETH.s.sol new file mode 100644 index 000000000000..979869cb3ad0 --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployDelayedWETH.s.sol @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { Script } from "forge-std/Script.sol"; + +// Scripts +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Libraries +import { LibString } from "@solady/utils/LibString.sol"; + +// Interfaces +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; +import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; + +/// @title DeployDelayedWETH +contract DeployDelayedWETHInput is BaseDeployIO { + /// Required inputs. + string internal _release; + string internal _standardVersionsToml; + address public _proxyAdmin; + ISuperchainConfig public _superchainConfigProxy; + address public _delayedWethOwner; + uint256 public _delayedWethDelay; + + function set(bytes4 _sel, uint256 _value) public { + if (_sel == this.delayedWethDelay.selector) { + require(_value != 0, "DeployDelayedWETH: delayedWethDelay cannot be zero"); + _delayedWethDelay = _value; + } else { + revert("DeployDelayedWETH: unknown selector"); + } + } + + function set(bytes4 _sel, address _value) public { + if (_sel == this.proxyAdmin.selector) { + require(_value != address(0), "DeployDelayedWETH: proxyAdmin cannot be zero address"); + _proxyAdmin = _value; + } else if (_sel == this.superchainConfigProxy.selector) { + require(_value != address(0), "DeployDelayedWETH: superchainConfigProxy cannot be zero address"); + _superchainConfigProxy = ISuperchainConfig(_value); + } else if (_sel == this.delayedWethOwner.selector) { + require(_value != address(0), "DeployDelayedWETH: delayedWethOwner cannot be zero address"); + _delayedWethOwner = _value; + } else { + revert("DeployDelayedWETH: unknown selector"); + } + } + + function set(bytes4 _sel, string memory _value) public { + if (_sel == this.release.selector) { + require(!LibString.eq(_value, ""), "DeployDelayedWETH: release cannot be empty"); + _release = _value; + } else if (_sel == this.standardVersionsToml.selector) { + require(!LibString.eq(_value, ""), "DeployDelayedWETH: standardVersionsToml cannot be empty"); + _standardVersionsToml = _value; + } else { + revert("DeployDelayedWETH: unknown selector"); + } + } + + function release() public view returns (string memory) { + require(!LibString.eq(_release, ""), "DeployDelayedWETH: release not set"); + return _release; + } + + function standardVersionsToml() public view returns (string memory) { + require(!LibString.eq(_standardVersionsToml, ""), "DeployDelayedWETH: standardVersionsToml not set"); + return _standardVersionsToml; + } + + function proxyAdmin() public view returns (address) { + require(_proxyAdmin != address(0), "DeployDelayedWETH: proxyAdmin not set"); + return _proxyAdmin; + } + + function superchainConfigProxy() public view returns (ISuperchainConfig) { + require(address(_superchainConfigProxy) != address(0), "DeployDisputeGame: superchainConfigProxy not set"); + return _superchainConfigProxy; + } + + function delayedWethOwner() public view returns (address) { + require(_delayedWethOwner != address(0), "DeployDelayedWETH: delayedWethOwner not set"); + return _delayedWethOwner; + } + + function delayedWethDelay() public view returns (uint256) { + require(_delayedWethDelay != 0, "DeployDelayedWETH: delayedWethDelay not set"); + return _delayedWethDelay; + } +} + +/// @title DeployDelayedWETHOutput +contract DeployDelayedWETHOutput is BaseDeployIO { + IDelayedWETH internal _delayedWethImpl; + IDelayedWETH internal _delayedWethProxy; + + function set(bytes4 _sel, address _value) public { + if (_sel == this.delayedWethImpl.selector) { + require(_value != address(0), "DeployDelayedWETHOutput: delayedWethImpl cannot be zero address"); + _delayedWethImpl = IDelayedWETH(payable(_value)); + } else if (_sel == this.delayedWethProxy.selector) { + require(_value != address(0), "DeployDelayedWETHOutput: delayedWethProxy cannot be zero address"); + _delayedWethProxy = IDelayedWETH(payable(_value)); + } else { + revert("DeployDelayedWETHOutput: unknown selector"); + } + } + + function checkOutput(DeployDelayedWETHInput _dwi) public { + DeployUtils.assertValidContractAddress(address(_delayedWethImpl)); + DeployUtils.assertValidContractAddress(address(_delayedWethProxy)); + assertValidDeploy(_dwi); + } + + function delayedWethImpl() public view returns (IDelayedWETH) { + DeployUtils.assertValidContractAddress(address(_delayedWethImpl)); + return _delayedWethImpl; + } + + function delayedWethProxy() public view returns (IDelayedWETH) { + DeployUtils.assertValidContractAddress(address(_delayedWethProxy)); + return _delayedWethProxy; + } + + function assertValidDeploy(DeployDelayedWETHInput _dwi) public { + assertValidDelayedWethImpl(_dwi); + assertValidDelayedWethProxy(_dwi); + } + + function assertValidDelayedWethImpl(DeployDelayedWETHInput _dwi) internal { + IProxy proxy = IProxy(payable(address(delayedWethProxy()))); + vm.prank(address(0)); + address impl = proxy.implementation(); + require(impl == address(delayedWethImpl()), "DWI-10"); + DeployUtils.assertInitialized({ _contractAddress: address(delayedWethImpl()), _slot: 0, _offset: 0 }); + require(delayedWethImpl().owner() == address(0), "DWI-20"); + require(delayedWethImpl().delay() == _dwi.delayedWethDelay(), "DWI-30"); + require(address(delayedWethImpl().config()) == address(0), "DWI-30"); + } + + function assertValidDelayedWethProxy(DeployDelayedWETHInput _dwi) internal { + // Check as proxy. + IProxy proxy = IProxy(payable(address(delayedWethProxy()))); + vm.prank(address(0)); + address admin = proxy.admin(); + require(admin == _dwi.proxyAdmin(), "DWP-10"); + + // Check as implementation. + DeployUtils.assertInitialized({ _contractAddress: address(delayedWethProxy()), _slot: 0, _offset: 0 }); + require(delayedWethProxy().owner() == _dwi.delayedWethOwner(), "DWP-20"); + require(delayedWethProxy().delay() == _dwi.delayedWethDelay(), "DWP-30"); + require(delayedWethProxy().config() == _dwi.superchainConfigProxy(), "DWP-40"); + } +} + +/// @title DeployDelayedWETH +contract DeployDelayedWETH is Script { + function run(DeployDelayedWETHInput _dwi, DeployDelayedWETHOutput _dwo) public { + deployDelayedWethProxy(_dwi, _dwo); + _dwo.checkOutput(_dwi); + } + + function deployDelayedWethImpl(DeployDelayedWETHInput _dwi, DeployDelayedWETHOutput _dwo) internal { + string memory release = _dwi.release(); + string memory stdVerToml = _dwi.standardVersionsToml(); + string memory contractName = "delayed_weth"; + IDelayedWETH impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = IDelayedWETH(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = IDelayedWETH( + DeployUtils.create1({ + _name: "DelayedWETH", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IDelayedWETH.__constructor__, (_dwi.delayedWethDelay())) + ) + }) + ); + } else { + revert(string.concat("DeployDelayedWETH: failed to deploy release ", release)); + } + + vm.label(address(impl), "DelayedWETHImpl"); + _dwo.set(_dwo.delayedWethImpl.selector, address(impl)); + } + + function deployDelayedWethProxy(DeployDelayedWETHInput _dwi, DeployDelayedWETHOutput _dwo) internal { + vm.broadcast(msg.sender); + IProxy proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + }) + ); + + deployDelayedWethImpl(_dwi, _dwo); + IDelayedWETH impl = _dwo.delayedWethImpl(); + + vm.startBroadcast(msg.sender); + proxy.upgradeToAndCall( + address(impl), abi.encodeCall(impl.initialize, (_dwi.delayedWethOwner(), _dwi.superchainConfigProxy())) + ); + proxy.changeAdmin(_dwi.proxyAdmin()); + vm.stopBroadcast(); + + vm.label(address(proxy), "DelayedWETHProxy"); + _dwo.set(_dwo.delayedWethProxy.selector, address(proxy)); + } + + // Zero address is returned if the address is not found in '_standardVersionsToml'. + function getReleaseAddress( + string memory _version, + string memory _contractName, + string memory _standardVersionsToml + ) + internal + pure + returns (address addr_) + { + string memory baseKey = string.concat('.releases["', _version, '"].', _contractName); + string memory implAddressKey = string.concat(baseKey, ".implementation_address"); + string memory addressKey = string.concat(baseKey, ".address"); + try vm.parseTomlAddress(_standardVersionsToml, implAddressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + try vm.parseTomlAddress(_standardVersionsToml, addressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + addr_ = address(0); + } + } + } + + // A release is considered a 'develop' release if it does not start with 'op-contracts'. + function isDevelopRelease(string memory _release) internal pure returns (bool) { + return !LibString.startsWith(_release, "op-contracts"); + } +} From 5e209a5256b2f011a99bc12a83e126a29a5cc3d6 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 1 Nov 2024 03:24:18 +0700 Subject: [PATCH 092/451] op-e2e: interop cross L2 inbox system test (#12754) * op-e2e: interop cross L2 inbox system test Co-authored-by: Axel Kingsley * point at latest interop op-geth commit * lint * test fixes * update to latest geth commit * op-supervisor: uint32 log index, improve chainid test * correct uint64 to uint32 everywhere ; point at latest geth commit * use tagged geth version --------- Co-authored-by: Axel Kingsley --- go.mod | 4 +- go.sum | 4 +- op-chain-ops/interopgen/recipe.go | 1 + .../deployer/integration_test/apply_test.go | 2 +- .../contracts/{ => bindings/emit}/emit.go | 5 +- .../interop/contracts/bindings/inbox/inbox.go | 231 ++++++++++++++++++ .../bindings/systemconfig/systemconfig.go | 201 +++++++++++++++ .../build/ICrossL2Inbox.sol/ICrossL2Inbox.abi | 97 ++++++++ .../build/ICrossL2Inbox.sol/ICrossL2Inbox.bin | 1 + .../ICrossL2Inbox.sol/ICrossL2Inbox.json | 1 + .../build/ISystemConfig.sol/ISystemConfig.abi | 15 ++ .../build/ISystemConfig.sol/ISystemConfig.bin | 1 + .../ISystemConfig.sol/ISystemConfig.json | 1 + .../contracts/build/emit.sol/EmitEvent.abi | 28 +++ .../contracts/build/emit.sol/EmitEvent.bin | 1 + .../contracts/build/emit.sol/EmitEvent.json | 1 + op-e2e/interop/contracts/generate.sh | 19 +- .../interop/contracts/src/ICrossL2Inbox.sol | 29 +++ .../interop/contracts/src/ISystemConfig.sol | 8 + op-e2e/interop/interop_test.go | 110 ++++++++- op-e2e/interop/supersystem.go | 147 ++++++++++- op-supervisor/supervisor/backend/backend.go | 19 +- .../supervisor/backend/db/logs/db.go | 2 +- .../backend/processors/log_processor.go | 14 +- op-supervisor/supervisor/service.go | 1 + op-supervisor/supervisor/types/types.go | 25 +- op-supervisor/supervisor/types/types_test.go | 16 +- .../src/L1/OPContractsManagerInterop.sol | 4 +- 28 files changed, 946 insertions(+), 42 deletions(-) rename op-e2e/interop/contracts/{ => bindings/emit}/emit.go (99%) create mode 100644 op-e2e/interop/contracts/bindings/inbox/inbox.go create mode 100644 op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go create mode 100644 op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi create mode 100644 op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin create mode 100644 op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json create mode 100644 op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi create mode 100644 op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin create mode 100644 op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json create mode 100644 op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi create mode 100644 op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin create mode 100644 op-e2e/interop/contracts/build/emit.sol/EmitEvent.json create mode 100644 op-e2e/interop/contracts/src/ICrossL2Inbox.sol create mode 100644 op-e2e/interop/contracts/src/ISystemConfig.sol diff --git a/go.mod b/go.mod index 3232ef0d42ce..3ff8ebb344ed 100644 --- a/go.mod +++ b/go.mod @@ -250,9 +250,9 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.2 +replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 -//replace github.com/ethereum/go-ethereum => ../op-geth +//replace github.com/ethereum/go-ethereum => ../go-ethereum // replace github.com/ethereum-optimism/superchain-registry/superchain => ../superchain-registry/superchain diff --git a/go.sum b/go.sum index b183dc9ae47d..616fb9b7d453 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,8 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.2 h1:nOeSTzcFWUCvJO1MJ5AyI26dqR1F7vYgz2jNNKuEtoE= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.2/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 h1:LDSP85xTczjDYMBK0mOF5mzpZifLGz1TTW/6NgQsytc= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.5/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index 85f429eb793a..8983ee72da8e 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -109,6 +109,7 @@ func prefundL2Accounts(l1Cfg *L1Config, l2Cfg *L2Config, addrs devkeys.Addresses l1Cfg.Prefund[l2Cfg.BatchSenderAddress] = Ether(10_000_000) l1Cfg.Prefund[l2Cfg.Deployer] = Ether(10_000_000) l1Cfg.Prefund[l2Cfg.FinalSystemOwner] = Ether(10_000_000) + l1Cfg.Prefund[l2Cfg.SystemConfigOwner] = Ether(10_000_000) proposer, err := addrs.Address(devkeys.ChainOperatorKey{ ChainID: new(big.Int).SetUint64(l2Cfg.L2ChainID), Role: devkeys.ProposerRole, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index e0b34dc8ea6a..f123953a3ea4 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -400,7 +400,7 @@ func TestInteropDeployment(t *testing.T) { chainState := st.Chains[0] depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) - proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes()) + proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes()) checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) } diff --git a/op-e2e/interop/contracts/emit.go b/op-e2e/interop/contracts/bindings/emit/emit.go similarity index 99% rename from op-e2e/interop/contracts/emit.go rename to op-e2e/interop/contracts/bindings/emit/emit.go index ceae2412659b..7755ef5fd18f 100644 --- a/op-e2e/interop/contracts/emit.go +++ b/op-e2e/interop/contracts/bindings/emit/emit.go @@ -26,7 +26,6 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription - _ = abi.ConvertType ) // EmitMetaData contains all meta data concerning the Emit contract. @@ -157,11 +156,11 @@ func NewEmitFilterer(address common.Address, filterer bind.ContractFilterer) (*E // bindEmit binds a generic wrapper to an already deployed contract. func bindEmit(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := EmitMetaData.GetAbi() + parsed, err := abi.JSON(strings.NewReader(EmitABI)) if err != nil { return nil, err } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/op-e2e/interop/contracts/bindings/inbox/inbox.go b/op-e2e/interop/contracts/bindings/inbox/inbox.go new file mode 100644 index 000000000000..f73fd56355e4 --- /dev/null +++ b/op-e2e/interop/contracts/bindings/inbox/inbox.go @@ -0,0 +1,231 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package inbox + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// Identifier is an auto generated low-level Go binding around an user-defined struct. +type Identifier struct { + Origin common.Address + BlockNumber *big.Int + LogIndex *big.Int + Timestamp *big.Int + ChainId *big.Int +} + +// InboxMetaData contains all meta data concerning the Inbox contract. +var InboxMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"executeMessage\",\"inputs\":[{\"name\":\"_id\",\"type\":\"tuple\",\"internalType\":\"structIdentifier\",\"components\":[{\"name\":\"origin\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"logIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"timestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"chainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"_target\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_message\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"validateMessage\",\"inputs\":[{\"name\":\"_id\",\"type\":\"tuple\",\"internalType\":\"structIdentifier\",\"components\":[{\"name\":\"origin\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"logIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"timestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"chainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"_msgHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"}]", +} + +// InboxABI is the input ABI used to generate the binding from. +// Deprecated: Use InboxMetaData.ABI instead. +var InboxABI = InboxMetaData.ABI + +// Inbox is an auto generated Go binding around an Ethereum contract. +type Inbox struct { + InboxCaller // Read-only binding to the contract + InboxTransactor // Write-only binding to the contract + InboxFilterer // Log filterer for contract events +} + +// InboxCaller is an auto generated read-only Go binding around an Ethereum contract. +type InboxCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// InboxTransactor is an auto generated write-only Go binding around an Ethereum contract. +type InboxTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// InboxFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type InboxFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// InboxSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type InboxSession struct { + Contract *Inbox // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// InboxCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type InboxCallerSession struct { + Contract *InboxCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// InboxTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type InboxTransactorSession struct { + Contract *InboxTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// InboxRaw is an auto generated low-level Go binding around an Ethereum contract. +type InboxRaw struct { + Contract *Inbox // Generic contract binding to access the raw methods on +} + +// InboxCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type InboxCallerRaw struct { + Contract *InboxCaller // Generic read-only contract binding to access the raw methods on +} + +// InboxTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type InboxTransactorRaw struct { + Contract *InboxTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewInbox creates a new instance of Inbox, bound to a specific deployed contract. +func NewInbox(address common.Address, backend bind.ContractBackend) (*Inbox, error) { + contract, err := bindInbox(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Inbox{InboxCaller: InboxCaller{contract: contract}, InboxTransactor: InboxTransactor{contract: contract}, InboxFilterer: InboxFilterer{contract: contract}}, nil +} + +// NewInboxCaller creates a new read-only instance of Inbox, bound to a specific deployed contract. +func NewInboxCaller(address common.Address, caller bind.ContractCaller) (*InboxCaller, error) { + contract, err := bindInbox(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &InboxCaller{contract: contract}, nil +} + +// NewInboxTransactor creates a new write-only instance of Inbox, bound to a specific deployed contract. +func NewInboxTransactor(address common.Address, transactor bind.ContractTransactor) (*InboxTransactor, error) { + contract, err := bindInbox(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &InboxTransactor{contract: contract}, nil +} + +// NewInboxFilterer creates a new log filterer instance of Inbox, bound to a specific deployed contract. +func NewInboxFilterer(address common.Address, filterer bind.ContractFilterer) (*InboxFilterer, error) { + contract, err := bindInbox(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &InboxFilterer{contract: contract}, nil +} + +// bindInbox binds a generic wrapper to an already deployed contract. +func bindInbox(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(InboxABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Inbox *InboxRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Inbox.Contract.InboxCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Inbox *InboxRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Inbox.Contract.InboxTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Inbox *InboxRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Inbox.Contract.InboxTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Inbox *InboxCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Inbox.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Inbox *InboxTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Inbox.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Inbox *InboxTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Inbox.Contract.contract.Transact(opts, method, params...) +} + +// ExecuteMessage is a paid mutator transaction binding the contract method 0x5984c53e. +// +// Solidity: function executeMessage((address,uint256,uint256,uint256,uint256) _id, address _target, bytes _message) payable returns() +func (_Inbox *InboxTransactor) ExecuteMessage(opts *bind.TransactOpts, _id Identifier, _target common.Address, _message []byte) (*types.Transaction, error) { + return _Inbox.contract.Transact(opts, "executeMessage", _id, _target, _message) +} + +// ExecuteMessage is a paid mutator transaction binding the contract method 0x5984c53e. +// +// Solidity: function executeMessage((address,uint256,uint256,uint256,uint256) _id, address _target, bytes _message) payable returns() +func (_Inbox *InboxSession) ExecuteMessage(_id Identifier, _target common.Address, _message []byte) (*types.Transaction, error) { + return _Inbox.Contract.ExecuteMessage(&_Inbox.TransactOpts, _id, _target, _message) +} + +// ExecuteMessage is a paid mutator transaction binding the contract method 0x5984c53e. +// +// Solidity: function executeMessage((address,uint256,uint256,uint256,uint256) _id, address _target, bytes _message) payable returns() +func (_Inbox *InboxTransactorSession) ExecuteMessage(_id Identifier, _target common.Address, _message []byte) (*types.Transaction, error) { + return _Inbox.Contract.ExecuteMessage(&_Inbox.TransactOpts, _id, _target, _message) +} + +// ValidateMessage is a paid mutator transaction binding the contract method 0xab4d6f75. +// +// Solidity: function validateMessage((address,uint256,uint256,uint256,uint256) _id, bytes32 _msgHash) returns() +func (_Inbox *InboxTransactor) ValidateMessage(opts *bind.TransactOpts, _id Identifier, _msgHash [32]byte) (*types.Transaction, error) { + return _Inbox.contract.Transact(opts, "validateMessage", _id, _msgHash) +} + +// ValidateMessage is a paid mutator transaction binding the contract method 0xab4d6f75. +// +// Solidity: function validateMessage((address,uint256,uint256,uint256,uint256) _id, bytes32 _msgHash) returns() +func (_Inbox *InboxSession) ValidateMessage(_id Identifier, _msgHash [32]byte) (*types.Transaction, error) { + return _Inbox.Contract.ValidateMessage(&_Inbox.TransactOpts, _id, _msgHash) +} + +// ValidateMessage is a paid mutator transaction binding the contract method 0xab4d6f75. +// +// Solidity: function validateMessage((address,uint256,uint256,uint256,uint256) _id, bytes32 _msgHash) returns() +func (_Inbox *InboxTransactorSession) ValidateMessage(_id Identifier, _msgHash [32]byte) (*types.Transaction, error) { + return _Inbox.Contract.ValidateMessage(&_Inbox.TransactOpts, _id, _msgHash) +} diff --git a/op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go b/op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go new file mode 100644 index 000000000000..9e29cc4700e5 --- /dev/null +++ b/op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go @@ -0,0 +1,201 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package systemconfig + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// SystemconfigMetaData contains all meta data concerning the Systemconfig contract. +var SystemconfigMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"addDependency\",\"inputs\":[{\"name\":\"_chainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"}]", +} + +// SystemconfigABI is the input ABI used to generate the binding from. +// Deprecated: Use SystemconfigMetaData.ABI instead. +var SystemconfigABI = SystemconfigMetaData.ABI + +// Systemconfig is an auto generated Go binding around an Ethereum contract. +type Systemconfig struct { + SystemconfigCaller // Read-only binding to the contract + SystemconfigTransactor // Write-only binding to the contract + SystemconfigFilterer // Log filterer for contract events +} + +// SystemconfigCaller is an auto generated read-only Go binding around an Ethereum contract. +type SystemconfigCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SystemconfigTransactor is an auto generated write-only Go binding around an Ethereum contract. +type SystemconfigTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SystemconfigFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type SystemconfigFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SystemconfigSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type SystemconfigSession struct { + Contract *Systemconfig // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SystemconfigCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type SystemconfigCallerSession struct { + Contract *SystemconfigCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// SystemconfigTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type SystemconfigTransactorSession struct { + Contract *SystemconfigTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SystemconfigRaw is an auto generated low-level Go binding around an Ethereum contract. +type SystemconfigRaw struct { + Contract *Systemconfig // Generic contract binding to access the raw methods on +} + +// SystemconfigCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type SystemconfigCallerRaw struct { + Contract *SystemconfigCaller // Generic read-only contract binding to access the raw methods on +} + +// SystemconfigTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type SystemconfigTransactorRaw struct { + Contract *SystemconfigTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewSystemconfig creates a new instance of Systemconfig, bound to a specific deployed contract. +func NewSystemconfig(address common.Address, backend bind.ContractBackend) (*Systemconfig, error) { + contract, err := bindSystemconfig(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Systemconfig{SystemconfigCaller: SystemconfigCaller{contract: contract}, SystemconfigTransactor: SystemconfigTransactor{contract: contract}, SystemconfigFilterer: SystemconfigFilterer{contract: contract}}, nil +} + +// NewSystemconfigCaller creates a new read-only instance of Systemconfig, bound to a specific deployed contract. +func NewSystemconfigCaller(address common.Address, caller bind.ContractCaller) (*SystemconfigCaller, error) { + contract, err := bindSystemconfig(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SystemconfigCaller{contract: contract}, nil +} + +// NewSystemconfigTransactor creates a new write-only instance of Systemconfig, bound to a specific deployed contract. +func NewSystemconfigTransactor(address common.Address, transactor bind.ContractTransactor) (*SystemconfigTransactor, error) { + contract, err := bindSystemconfig(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SystemconfigTransactor{contract: contract}, nil +} + +// NewSystemconfigFilterer creates a new log filterer instance of Systemconfig, bound to a specific deployed contract. +func NewSystemconfigFilterer(address common.Address, filterer bind.ContractFilterer) (*SystemconfigFilterer, error) { + contract, err := bindSystemconfig(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SystemconfigFilterer{contract: contract}, nil +} + +// bindSystemconfig binds a generic wrapper to an already deployed contract. +func bindSystemconfig(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(SystemconfigABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Systemconfig *SystemconfigRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Systemconfig.Contract.SystemconfigCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Systemconfig *SystemconfigRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Systemconfig.Contract.SystemconfigTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Systemconfig *SystemconfigRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Systemconfig.Contract.SystemconfigTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Systemconfig *SystemconfigCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Systemconfig.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Systemconfig *SystemconfigTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Systemconfig.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Systemconfig *SystemconfigTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Systemconfig.Contract.contract.Transact(opts, method, params...) +} + +// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c. +// +// Solidity: function addDependency(uint256 _chainId) returns() +func (_Systemconfig *SystemconfigTransactor) AddDependency(opts *bind.TransactOpts, _chainId *big.Int) (*types.Transaction, error) { + return _Systemconfig.contract.Transact(opts, "addDependency", _chainId) +} + +// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c. +// +// Solidity: function addDependency(uint256 _chainId) returns() +func (_Systemconfig *SystemconfigSession) AddDependency(_chainId *big.Int) (*types.Transaction, error) { + return _Systemconfig.Contract.AddDependency(&_Systemconfig.TransactOpts, _chainId) +} + +// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c. +// +// Solidity: function addDependency(uint256 _chainId) returns() +func (_Systemconfig *SystemconfigTransactorSession) AddDependency(_chainId *big.Int) (*types.Transaction, error) { + return _Systemconfig.Contract.AddDependency(&_Systemconfig.TransactOpts, _chainId) +} diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi new file mode 100644 index 000000000000..b724169ec6fa --- /dev/null +++ b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi @@ -0,0 +1,97 @@ +[ + { + "type": "function", + "name": "executeMessage", + "inputs": [ + { + "name": "_id", + "type": "tuple", + "internalType": "struct Identifier", + "components": [ + { + "name": "origin", + "type": "address", + "internalType": "address" + }, + { + "name": "blockNumber", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "logIndex", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "timestamp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "_target", + "type": "address", + "internalType": "address" + }, + { + "name": "_message", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "validateMessage", + "inputs": [ + { + "name": "_id", + "type": "tuple", + "internalType": "struct Identifier", + "components": [ + { + "name": "origin", + "type": "address", + "internalType": "address" + }, + { + "name": "blockNumber", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "logIndex", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "timestamp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "_msgHash", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } +] diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin new file mode 100644 index 000000000000..ec687260b851 --- /dev/null +++ b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin @@ -0,0 +1 @@ +0x diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json new file mode 100644 index 000000000000..96c8d6ddb036 --- /dev/null +++ b/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"executeMessage","inputs":[{"name":"_id","type":"tuple","internalType":"struct Identifier","components":[{"name":"origin","type":"address","internalType":"address"},{"name":"blockNumber","type":"uint256","internalType":"uint256"},{"name":"logIndex","type":"uint256","internalType":"uint256"},{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"chainId","type":"uint256","internalType":"uint256"}]},{"name":"_target","type":"address","internalType":"address"},{"name":"_message","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"validateMessage","inputs":[{"name":"_id","type":"tuple","internalType":"struct Identifier","components":[{"name":"origin","type":"address","internalType":"address"},{"name":"blockNumber","type":"uint256","internalType":"uint256"},{"name":"logIndex","type":"uint256","internalType":"uint256"},{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"chainId","type":"uint256","internalType":"uint256"}]},{"name":"_msgHash","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)":"5984c53e","validateMessage((address,uint256,uint256,uint256,uint256),bytes32)":"ab4d6f75"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"origin\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"logIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Identifier\",\"name\":\"_id\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_message\",\"type\":\"bytes\"}],\"name\":\"executeMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"origin\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"logIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Identifier\",\"name\":\"_id\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"_msgHash\",\"type\":\"bytes32\"}],\"name\":\"validateMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)\":{\"params\":{\"_id\":\"An Identifier pointing to the initiating message.\",\"_message\":\"The message payload, matching the initiating message.\",\"_target\":\"Account that is called with _msg.\"}},\"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)\":{\"params\":{\"_id\":\"Identifier of the message.\",\"_msgHash\":\"Hash of the message payload to call target with.\"}}},\"title\":\"ICrossL2Inbox\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)\":{\"notice\":\"Executes a cross chain message on the destination chain.\"},\"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)\":{\"notice\":\"Validates a cross chain message on the destination chain and emits an ExecutingMessage event. This function is useful for applications that understand the schema of the _message payload and want to process it in a custom way.\"}},\"notice\":\"Interface for the CrossL2Inbox contract.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ICrossL2Inbox.sol\":\"ICrossL2Inbox\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"src/ICrossL2Inbox.sol\":{\"keccak256\":\"0x97daf76e4a10b96d8062e71df352cbfa7577593fa96676fd43b40f4a0aca9b7f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://74518c974318070da4e118cd28aa86d7f0702ed8cccc39d60906b93197687248\",\"dweb:/ipfs/QmWimBXYsDWDtNgzhjaLUx4xEiTMvbx5635E2TfNBKoV1E\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"struct Identifier","name":"_id","type":"tuple","components":[{"internalType":"address","name":"origin","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"logIndex","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"chainId","type":"uint256"}]},{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_message","type":"bytes"}],"stateMutability":"payable","type":"function","name":"executeMessage"},{"inputs":[{"internalType":"struct Identifier","name":"_id","type":"tuple","components":[{"internalType":"address","name":"origin","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"logIndex","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"chainId","type":"uint256"}]},{"internalType":"bytes32","name":"_msgHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"validateMessage"}],"devdoc":{"kind":"dev","methods":{"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)":{"params":{"_id":"An Identifier pointing to the initiating message.","_message":"The message payload, matching the initiating message.","_target":"Account that is called with _msg."}},"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)":{"params":{"_id":"Identifier of the message.","_msgHash":"Hash of the message payload to call target with."}}},"version":1},"userdoc":{"kind":"user","methods":{"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)":{"notice":"Executes a cross chain message on the destination chain."},"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)":{"notice":"Validates a cross chain message on the destination chain and emits an ExecutingMessage event. This function is useful for applications that understand the schema of the _message payload and want to process it in a custom way."}},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/ICrossL2Inbox.sol":"ICrossL2Inbox"},"evmVersion":"cancun","libraries":{}},"sources":{"src/ICrossL2Inbox.sol":{"keccak256":"0x97daf76e4a10b96d8062e71df352cbfa7577593fa96676fd43b40f4a0aca9b7f","urls":["bzz-raw://74518c974318070da4e118cd28aa86d7f0702ed8cccc39d60906b93197687248","dweb:/ipfs/QmWimBXYsDWDtNgzhjaLUx4xEiTMvbx5635E2TfNBKoV1E"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user","methods":{"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)":{"notice":"Executes a cross chain message on the destination chain."},"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)":{"notice":"Validates a cross chain message on the destination chain and emits an ExecutingMessage event. This function is useful for applications that understand the schema of the _message payload and want to process it in a custom way."}},"notice":"Interface for the CrossL2Inbox contract."},"devdoc":{"version":1,"kind":"dev","methods":{"executeMessage((address,uint256,uint256,uint256,uint256),address,bytes)":{"params":{"_id":"An Identifier pointing to the initiating message.","_message":"The message payload, matching the initiating message.","_target":"Account that is called with _msg."}},"validateMessage((address,uint256,uint256,uint256,uint256),bytes32)":{"params":{"_id":"Identifier of the message.","_msgHash":"Hash of the message payload to call target with."}}},"title":"ICrossL2Inbox"},"ast":{"absolutePath":"src/ICrossL2Inbox.sol","id":35,"exportedSymbols":{"ICrossL2Inbox":[34],"Identifier":[12]},"nodeType":"SourceUnit","src":"32:1153:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","^","0.8",".0"]},{"id":12,"nodeType":"StructDefinition","src":"57:132:0","nodes":[],"canonicalName":"Identifier","members":[{"constant":false,"id":3,"mutability":"mutable","name":"origin","nameLocation":"89:6:0","nodeType":"VariableDeclaration","scope":12,"src":"81:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":2,"name":"address","nodeType":"ElementaryTypeName","src":"81:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"blockNumber","nameLocation":"109:11:0","nodeType":"VariableDeclaration","scope":12,"src":"101:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":4,"name":"uint256","nodeType":"ElementaryTypeName","src":"101:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"constant":false,"id":7,"mutability":"mutable","name":"logIndex","nameLocation":"134:8:0","nodeType":"VariableDeclaration","scope":12,"src":"126:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":6,"name":"uint256","nodeType":"ElementaryTypeName","src":"126:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"constant":false,"id":9,"mutability":"mutable","name":"timestamp","nameLocation":"156:9:0","nodeType":"VariableDeclaration","scope":12,"src":"148:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":8,"name":"uint256","nodeType":"ElementaryTypeName","src":"148:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"constant":false,"id":11,"mutability":"mutable","name":"chainId","nameLocation":"179:7:0","nodeType":"VariableDeclaration","scope":12,"src":"171:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":10,"name":"uint256","nodeType":"ElementaryTypeName","src":"171:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"name":"Identifier","nameLocation":"64:10:0","scope":35,"visibility":"public"},{"id":34,"nodeType":"ContractDefinition","src":"269:915:0","nodes":[{"id":24,"nodeType":"FunctionDefinition","src":"577:108:0","nodes":[],"documentation":{"id":14,"nodeType":"StructuredDocumentation","src":"300:272:0","text":"@notice Executes a cross chain message on the destination chain.\n @param _id An Identifier pointing to the initiating message.\n @param _target Account that is called with _msg.\n @param _message The message payload, matching the initiating message."},"functionSelector":"5984c53e","implemented":false,"kind":"function","modifiers":[],"name":"executeMessage","nameLocation":"586:14:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":17,"mutability":"mutable","name":"_id","nameLocation":"621:3:0","nodeType":"VariableDeclaration","scope":24,"src":"601:23:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Identifier_$12_calldata_ptr","typeString":"struct Identifier"},"typeName":{"id":16,"nodeType":"UserDefinedTypeName","pathNode":{"id":15,"name":"Identifier","nameLocations":["601:10:0"],"nodeType":"IdentifierPath","referencedDeclaration":12,"src":"601:10:0"},"referencedDeclaration":12,"src":"601:10:0","typeDescriptions":{"typeIdentifier":"t_struct$_Identifier_$12_storage_ptr","typeString":"struct Identifier"}},"visibility":"internal"},{"constant":false,"id":19,"mutability":"mutable","name":"_target","nameLocation":"634:7:0","nodeType":"VariableDeclaration","scope":24,"src":"626:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":18,"name":"address","nodeType":"ElementaryTypeName","src":"626:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"_message","nameLocation":"658:8:0","nodeType":"VariableDeclaration","scope":24,"src":"643:23:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":20,"name":"bytes","nodeType":"ElementaryTypeName","src":"643:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"600:67:0"},"returnParameters":{"id":23,"nodeType":"ParameterList","parameters":[],"src":"684:0:0"},"scope":34,"stateMutability":"payable","virtual":false,"visibility":"external"},{"id":33,"nodeType":"FunctionDefinition","src":"1105:77:0","nodes":[],"documentation":{"id":25,"nodeType":"StructuredDocumentation","src":"691:409:0","text":"@notice Validates a cross chain message on the destination chain\n and emits an ExecutingMessage event. This function is useful\n for applications that understand the schema of the _message payload and want to\n process it in a custom way.\n @param _id Identifier of the message.\n @param _msgHash Hash of the message payload to call target with."},"functionSelector":"ab4d6f75","implemented":false,"kind":"function","modifiers":[],"name":"validateMessage","nameLocation":"1114:15:0","parameters":{"id":31,"nodeType":"ParameterList","parameters":[{"constant":false,"id":28,"mutability":"mutable","name":"_id","nameLocation":"1150:3:0","nodeType":"VariableDeclaration","scope":33,"src":"1130:23:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Identifier_$12_calldata_ptr","typeString":"struct Identifier"},"typeName":{"id":27,"nodeType":"UserDefinedTypeName","pathNode":{"id":26,"name":"Identifier","nameLocations":["1130:10:0"],"nodeType":"IdentifierPath","referencedDeclaration":12,"src":"1130:10:0"},"referencedDeclaration":12,"src":"1130:10:0","typeDescriptions":{"typeIdentifier":"t_struct$_Identifier_$12_storage_ptr","typeString":"struct Identifier"}},"visibility":"internal"},{"constant":false,"id":30,"mutability":"mutable","name":"_msgHash","nameLocation":"1163:8:0","nodeType":"VariableDeclaration","scope":33,"src":"1155:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"},"typeName":{"id":29,"name":"bytes32","nodeType":"ElementaryTypeName","src":"1155:7:0","typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}},"visibility":"internal"}],"src":"1129:43:0"},"returnParameters":{"id":32,"nodeType":"ParameterList","parameters":[],"src":"1181:0:0"},"scope":34,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ICrossL2Inbox","contractDependencies":[],"contractKind":"interface","documentation":{"id":13,"nodeType":"StructuredDocumentation","src":"191:78:0","text":"@title ICrossL2Inbox\n @notice Interface for the CrossL2Inbox contract."},"fullyImplemented":false,"linearizedBaseContracts":[34],"name":"ICrossL2Inbox","nameLocation":"279:13:0","scope":35,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0} \ No newline at end of file diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi new file mode 100644 index 000000000000..7a7bffc2ca65 --- /dev/null +++ b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi @@ -0,0 +1,15 @@ +[ + { + "type": "function", + "name": "addDependency", + "inputs": [ + { + "name": "_chainId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } +] diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin new file mode 100644 index 000000000000..ec687260b851 --- /dev/null +++ b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin @@ -0,0 +1 @@ +0x diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json new file mode 100644 index 000000000000..fef67d0a40fd --- /dev/null +++ b/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"addDependency","inputs":[{"name":"_chainId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"addDependency(uint256)":"a89c793c"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_chainId\",\"type\":\"uint256\"}],\"name\":\"addDependency\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addDependency(uint256)\":{\"params\":{\"_chainId\":\"Chain ID of chain to add.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"addDependency(uint256)\":{\"notice\":\"Adds a chain to the interop dependency set. Can only be called by the dependency manager.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ISystemConfig.sol\":\"ISystemConfig\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"src/ISystemConfig.sol\":{\"keccak256\":\"0x764ea7e528e9e5ab907515b4da22738a49a3d5d8ffb948def86d0cbb9e7765be\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9307f5fd1e826f8d059f4aa9141c360aef54612ba8562ec996e7f078ef1d1cf2\",\"dweb:/ipfs/QmbzFSWWLoQUyeuD6U7detgjPy36L3DHJp9BPpaUZqVs68\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"addDependency"}],"devdoc":{"kind":"dev","methods":{"addDependency(uint256)":{"params":{"_chainId":"Chain ID of chain to add."}}},"version":1},"userdoc":{"kind":"user","methods":{"addDependency(uint256)":{"notice":"Adds a chain to the interop dependency set. Can only be called by the dependency manager."}},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/ISystemConfig.sol":"ISystemConfig"},"evmVersion":"cancun","libraries":{}},"sources":{"src/ISystemConfig.sol":{"keccak256":"0x764ea7e528e9e5ab907515b4da22738a49a3d5d8ffb948def86d0cbb9e7765be","urls":["bzz-raw://9307f5fd1e826f8d059f4aa9141c360aef54612ba8562ec996e7f078ef1d1cf2","dweb:/ipfs/QmbzFSWWLoQUyeuD6U7detgjPy36L3DHJp9BPpaUZqVs68"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user","methods":{"addDependency(uint256)":{"notice":"Adds a chain to the interop dependency set. Can only be called by the dependency manager."}}},"devdoc":{"version":1,"kind":"dev","methods":{"addDependency(uint256)":{"params":{"_chainId":"Chain ID of chain to add."}}}},"ast":{"absolutePath":"src/ISystemConfig.sol","id":9,"exportedSymbols":{"ISystemConfig":[8]},"nodeType":"SourceUnit","src":"32:264:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","^","0.8",".0"]},{"id":8,"nodeType":"ContractDefinition","src":"57:238:0","nodes":[{"id":7,"nodeType":"FunctionDefinition","src":"243:50:0","nodes":[],"documentation":{"id":2,"nodeType":"StructuredDocumentation","src":"87:151:0","text":"@notice Adds a chain to the interop dependency set. Can only be called by the dependency manager.\n @param _chainId Chain ID of chain to add."},"functionSelector":"a89c793c","implemented":false,"kind":"function","modifiers":[],"name":"addDependency","nameLocation":"252:13:0","parameters":{"id":5,"nodeType":"ParameterList","parameters":[{"constant":false,"id":4,"mutability":"mutable","name":"_chainId","nameLocation":"274:8:0","nodeType":"VariableDeclaration","scope":7,"src":"266:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":3,"name":"uint256","nodeType":"ElementaryTypeName","src":"266:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"265:18:0"},"returnParameters":{"id":6,"nodeType":"ParameterList","parameters":[],"src":"292:0:0"},"scope":8,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ISystemConfig","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[8],"name":"ISystemConfig","nameLocation":"67:13:0","scope":9,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0} \ No newline at end of file diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi new file mode 100644 index 000000000000..d30fa8fca93b --- /dev/null +++ b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi @@ -0,0 +1,28 @@ +[ + { + "type": "function", + "name": "emitData", + "inputs": [ + { + "name": "_data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "DataEmitted", + "inputs": [ + { + "name": "_data", + "type": "bytes", + "indexed": true, + "internalType": "bytes" + } + ], + "anonymous": false + } +] diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin new file mode 100644 index 000000000000..f4c15ab05dca --- /dev/null +++ b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin @@ -0,0 +1 @@ +0x6080604052348015600e575f80fd5b5060ff8061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.json b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.json new file mode 100644 index 000000000000..465823afa330 --- /dev/null +++ b/op-e2e/interop/contracts/build/emit.sol/EmitEvent.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"emitData","inputs":[{"name":"_data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"DataEmitted","inputs":[{"name":"_data","type":"bytes","indexed":true,"internalType":"bytes"}],"anonymous":false}],"bytecode":{"object":"0x6080604052348015600e575f80fd5b5060ff8061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a","sourceMap":"58:278:1:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a","sourceMap":"58:278:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;245:89;;;;;;:::i;:::-;;:::i;:::-;;;321:5;;309:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;245:89;;:::o;14:591:2:-;84:6;92;145:2;133:9;124:7;120:23;116:32;113:52;;;161:1;158;151:12;113:52;201:9;188:23;230:18;271:2;263:6;260:14;257:34;;;287:1;284;277:12;257:34;325:6;314:9;310:22;300:32;;370:7;363:4;359:2;355:13;351:27;341:55;;392:1;389;382:12;341:55;432:2;419:16;458:2;450:6;447:14;444:34;;;474:1;471;464:12;444:34;519:7;514:2;505:6;501:2;497:15;493:24;490:37;487:57;;;540:1;537;530:12;487:57;571:2;563:11;;;;;593:6;;-1:-1:-1;14:591:2;;-1:-1:-1;;;;14:591:2:o;610:271::-;793:6;785;780:3;767:33;749:3;819:16;;844:13;;;819:16;610:271;-1:-1:-1;610:271:2:o","linkReferences":{}},"methodIdentifiers":{"emitData(bytes)":"d836083e"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"DataEmitted\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"emitData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/emit.sol\":\"EmitEvent\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"src/emit.sol\":{\"keccak256\":\"0xe078378fd445ed0cbbf1087c5013110412ab6a44850af24a15bcde945467accc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://66c218de0059688c75903c2ba6d4066661dc6f5fa17a329dd7c385d151f3993a\",\"dweb:/ipfs/Qmby4S4N44naZ2miw3Tgdpq5Qbj4DwzJbcGapgqtd7qd26\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes","indexed":true}],"type":"event","name":"DataEmitted","anonymous":false},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"emitData"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/emit.sol":"EmitEvent"},"evmVersion":"cancun","libraries":{}},"sources":{"src/emit.sol":{"keccak256":"0xe078378fd445ed0cbbf1087c5013110412ab6a44850af24a15bcde945467accc","urls":["bzz-raw://66c218de0059688c75903c2ba6d4066661dc6f5fa17a329dd7c385d151f3993a","dweb:/ipfs/Qmby4S4N44naZ2miw3Tgdpq5Qbj4DwzJbcGapgqtd7qd26"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"src/emit.sol","id":52,"exportedSymbols":{"EmitEvent":[51]},"nodeType":"SourceUnit","src":"32:305:1","nodes":[{"id":36,"nodeType":"PragmaDirective","src":"32:24:1","nodes":[],"literals":["solidity","^","0.8",".15"]},{"id":51,"nodeType":"ContractDefinition","src":"58:278:1","nodes":[{"id":40,"nodeType":"EventDefinition","src":"133:39:1","nodes":[],"anonymous":false,"eventSelector":"e00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c","name":"DataEmitted","nameLocation":"139:11:1","parameters":{"id":39,"nodeType":"ParameterList","parameters":[{"constant":false,"id":38,"indexed":true,"mutability":"mutable","name":"_data","nameLocation":"165:5:1","nodeType":"VariableDeclaration","scope":40,"src":"151:19:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":37,"name":"bytes","nodeType":"ElementaryTypeName","src":"151:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"150:21:1"}},{"id":50,"nodeType":"FunctionDefinition","src":"245:89:1","nodes":[],"body":{"id":49,"nodeType":"Block","src":"294:40:1","nodes":[],"statements":[{"eventCall":{"arguments":[{"id":46,"name":"_data","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":42,"src":"321:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes calldata"}],"id":45,"name":"DataEmitted","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":40,"src":"309:11:1","typeDescriptions":{"typeIdentifier":"t_function_event_nonpayable$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory)"}},"id":47,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"nameLocations":[],"names":[],"nodeType":"FunctionCall","src":"309:18:1","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":48,"nodeType":"EmitStatement","src":"304:23:1"}]},"functionSelector":"d836083e","implemented":true,"kind":"function","modifiers":[],"name":"emitData","nameLocation":"254:8:1","parameters":{"id":43,"nodeType":"ParameterList","parameters":[{"constant":false,"id":42,"mutability":"mutable","name":"_data","nameLocation":"278:5:1","nodeType":"VariableDeclaration","scope":50,"src":"263:20:1","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":41,"name":"bytes","nodeType":"ElementaryTypeName","src":"263:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"262:22:1"},"returnParameters":{"id":44,"nodeType":"ParameterList","parameters":[],"src":"294:0:1"},"scope":51,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"EmitEvent","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[51],"name":"EmitEvent","nameLocation":"67:9:1","scope":52,"usedErrors":[],"usedEvents":[40]}],"license":"MIT"},"id":1} \ No newline at end of file diff --git a/op-e2e/interop/contracts/generate.sh b/op-e2e/interop/contracts/generate.sh index bba960153ec6..d6107df9cc86 100755 --- a/op-e2e/interop/contracts/generate.sh +++ b/op-e2e/interop/contracts/generate.sh @@ -9,4 +9,21 @@ cat EmitEvent.json | jq -r '.bytecode.object' > EmitEvent.bin cat EmitEvent.json | jq '.abi' > EmitEvent.abi cd ../.. -abigen --abi ./build/emit.sol/EmitEvent.abi --bin ./build/emit.sol/EmitEvent.bin --pkg emit --out ./emit.go +mkdir -p bindings/emit +abigen --abi ./build/emit.sol/EmitEvent.abi --bin ./build/emit.sol/EmitEvent.bin --pkg emit --out ./bindings/emit/emit.go + +cd build/ICrossL2Inbox.sol +cat ICrossL2Inbox.json | jq -r '.bytecode.object' > ICrossL2Inbox.bin +cat ICrossL2Inbox.json | jq '.abi' > ICrossL2Inbox.abi +cd ../.. + +mkdir -p bindings/inbox +abigen --abi ./build/ICrossL2Inbox.sol/ICrossL2Inbox.abi --bin ./build/ICrossL2Inbox.sol/ICrossL2Inbox.bin --pkg inbox --out ./bindings/inbox/inbox.go + +cd build/ISystemConfig.sol +cat ISystemConfig.json | jq -r '.bytecode.object' > ISystemConfig.bin +cat ISystemConfig.json | jq '.abi' > ISystemConfig.abi +cd ../.. + +mkdir -p bindings/systemconfig +abigen --abi ./build/ISystemConfig.sol/ISystemConfig.abi --bin ./build/ISystemConfig.sol/ISystemConfig.bin --pkg systemconfig --out ./bindings/systemconfig/systemconfig.go diff --git a/op-e2e/interop/contracts/src/ICrossL2Inbox.sol b/op-e2e/interop/contracts/src/ICrossL2Inbox.sol new file mode 100644 index 000000000000..780b43d9753b --- /dev/null +++ b/op-e2e/interop/contracts/src/ICrossL2Inbox.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +struct Identifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; +} + +/// @title ICrossL2Inbox +/// @notice Interface for the CrossL2Inbox contract. +interface ICrossL2Inbox { + + /// @notice Executes a cross chain message on the destination chain. + /// @param _id An Identifier pointing to the initiating message. + /// @param _target Account that is called with _msg. + /// @param _message The message payload, matching the initiating message. + function executeMessage(Identifier calldata _id, address _target, bytes calldata _message) external payable; + + /// @notice Validates a cross chain message on the destination chain + /// and emits an ExecutingMessage event. This function is useful + /// for applications that understand the schema of the _message payload and want to + /// process it in a custom way. + /// @param _id Identifier of the message. + /// @param _msgHash Hash of the message payload to call target with. + function validateMessage(Identifier calldata _id, bytes32 _msgHash) external; +} diff --git a/op-e2e/interop/contracts/src/ISystemConfig.sol b/op-e2e/interop/contracts/src/ISystemConfig.sol new file mode 100644 index 000000000000..fd14f919b2d0 --- /dev/null +++ b/op-e2e/interop/contracts/src/ISystemConfig.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ISystemConfig { + /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. + /// @param _chainId Chain ID of chain to add. + function addDependency(uint256 _chainId) external; +} diff --git a/op-e2e/interop/interop_test.go b/op-e2e/interop/interop_test.go index 6724f468be51..65efa9b84209 100644 --- a/op-e2e/interop/interop_test.go +++ b/op-e2e/interop/interop_test.go @@ -7,6 +7,11 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-service/dial" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -157,8 +162,6 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { } msgPayload = append(msgPayload, log.Data...) expectedHash := common.BytesToHash(crypto.Keccak256(msgPayload)) - // convert payload hash to log hash - logHash := types.PayloadHashToLogHash(expectedHash, log.Address) // get block for the log (for timestamp) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) @@ -170,16 +173,16 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { identifier := types.Identifier{ Origin: log.Address, BlockNumber: log.BlockNumber, - LogIndex: uint64(log.Index), + LogIndex: uint32(log.Index), Timestamp: block.Time(), ChainID: types.ChainIDFromBig(s2.ChainID(chainID)), } - safety, error := supervisor.CheckMessage(context.Background(), + safety, err := supervisor.CheckMessage(context.Background(), identifier, - logHash, + expectedHash, ) - require.ErrorIs(t, error, expectedError) + require.ErrorIs(t, err, expectedError) // the supervisor could progress the safety level more quickly than we expect, // which is why we check for a minimum safety level require.True(t, safety.AtLeastAsSafe(expectedSafety), "log: %v should be at least %s, but is %s", log, expectedSafety.String(), safety.String()) @@ -194,3 +197,98 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { } setupAndRun(t, test) } + +func TestInteropBlockBuilding(t *testing.T) { + logger := testlog.Logger(t, log.LevelInfo) + oplog.SetGlobalLogHandler(logger.Handler()) + + test := func(t *testing.T, s2 SuperSystem) { + ids := s2.L2IDs() + chainA := ids[0] + chainB := ids[1] + // We will initiate on chain A, and execute on chain B + s2.DeployEmitterContract(chainA, "Alice") + + // Add chain A as dependency to chain B, + // such that we can execute a message on B that was initiated on A. + depRec := s2.AddDependency(chainB, s2.ChainID(chainA)) + t.Logf("Dependency set in L1 block %d", depRec.BlockNumber) + + rollupClA, err := dial.DialRollupClientWithTimeout(context.Background(), time.Second*15, logger, s2.OpNode(chainA).UserRPC().RPC()) + require.NoError(t, err) + + // Now wait for the dependency to be visible in the L2 (receipt needs to be picked up) + require.Eventually(t, func() bool { + status, err := rollupClA.SyncStatus(context.Background()) + require.NoError(t, err) + return status.CrossUnsafeL2.L1Origin.Number >= depRec.BlockNumber.Uint64() + }, time.Second*30, time.Second, "wait for L1 origin to match dependency L1 block") + t.Log("Dependency information has been processed in L2 block") + + // emit log on chain A + emitRec := s2.EmitData(chainA, "Alice", "hello world") + t.Logf("Emitted a log event in block %d", emitRec.BlockNumber.Uint64()) + + // Wait for initiating side to become cross-unsafe + require.Eventually(t, func() bool { + status, err := rollupClA.SyncStatus(context.Background()) + require.NoError(t, err) + return status.CrossUnsafeL2.Number >= emitRec.BlockNumber.Uint64() + }, time.Second*60, time.Second, "wait for emitted data to become cross-unsafe") + t.Logf("Reached cross-unsafe block %d", emitRec.BlockNumber.Uint64()) + + // Identify the log + require.Len(t, emitRec.Logs, 1) + ev := emitRec.Logs[0] + ethCl := s2.L2GethClient(chainA) + header, err := ethCl.HeaderByHash(context.Background(), emitRec.BlockHash) + require.NoError(t, err) + identifier := types.Identifier{ + Origin: ev.Address, + BlockNumber: ev.BlockNumber, + LogIndex: uint32(ev.Index), + Timestamp: header.Time, + ChainID: types.ChainIDFromBig(s2.ChainID(chainA)), + } + + msgPayload := types.LogToMessagePayload(ev) + payloadHash := crypto.Keccak256Hash(msgPayload) + logHash := types.PayloadHashToLogHash(payloadHash, identifier.Origin) + t.Logf("expected payload hash: %s", payloadHash) + t.Logf("expected log hash: %s", logHash) + + invalidPayload := []byte("test invalid message") + invalidPayloadHash := crypto.Keccak256Hash(invalidPayload) + invalidLogHash := types.PayloadHashToLogHash(invalidPayloadHash, identifier.Origin) + t.Logf("invalid payload hash: %s", invalidPayloadHash) + t.Logf("invalid log hash: %s", invalidLogHash) + + // submit executing txs on B + + t.Log("Testing invalid message") + { + bobAddr := s2.Address(chainA, "Bob") // direct it to a random account without code + ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + defer cancel() + // Send an executing message, but with different payload. + // We expect the miner to be unable to include this tx, and confirmation to thus time out. + _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload) + require.NotNil(t, err) + require.ErrorIs(t, err, ctx.Err()) + require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded) + } + + t.Log("Testing valid message now") + { + bobAddr := s2.Address(chainA, "Bob") // direct it to a random account without code + ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + defer cancel() + // Send an executing message with the correct identifier / payload + rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload) + require.NoError(t, err, "expecting tx to be confirmed") + t.Logf("confirmed executing msg in block %s", rec.BlockNumber) + } + t.Log("Done") + } + setupAndRun(t, test) +} diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index 2d063174bdae..9f78c543bdcf 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -11,6 +11,12 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/systemconfig" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum/go-ethereum/eth/ethconfig" + gn "github.com/ethereum/go-ethereum/node" + "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -33,7 +39,8 @@ import ( "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/opnode" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/services" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils" - emit "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts" + "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/emit" + "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/inbox" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" "github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/p2p" @@ -96,6 +103,17 @@ type SuperSystem interface { DeployEmitterContract(network string, username string) common.Address // Use the Emitter Contract to emit an Event Log EmitData(network string, username string, data string) *types.Receipt + // AddDependency adds a dependency (by chain ID) to the given chain + AddDependency(network string, dep *big.Int) *types.Receipt + // ExecuteMessage calls the CrossL2Inbox executeMessage function + ExecuteMessage( + ctx context.Context, + id string, + sender string, + msgIdentifier supervisortypes.Identifier, + target common.Address, + message []byte, + ) (*types.Receipt, error) // Access a contract on a network by name Contract(network string, contractName string) interface{} } @@ -121,6 +139,7 @@ type interopE2ESystem struct { beacon *fakebeacon.FakeBeacon l1 *geth.GethInstance l2s map[string]l2Set + l1GethClient *ethclient.Client l2GethClients map[string]*ethclient.Client supervisor *supervisor.SupervisorService superClient *sources.SupervisorClient @@ -200,6 +219,7 @@ func (s *interopE2ESystem) prepareL1() (*fakebeacon.FakeBeacon, *geth.GethInstan require.NoError(s.t, err) require.NoError(s.t, l1Geth.Node.Start()) s.t.Cleanup(func() { + s.t.Logf("Closing L1 geth") _ = l1Geth.Close() }) return bcn, l1Geth @@ -239,11 +259,17 @@ func (s *interopE2ESystem) newOperatorKeysForL2(l2Out *interopgen.L2Output) map[ func (s *interopE2ESystem) newGethForL2(id string, l2Out *interopgen.L2Output) *geth.GethInstance { jwtPath := writeDefaultJWT(s.t) name := "l2-" + id - l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath) + l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath, + func(ethCfg *ethconfig.Config, nodeCfg *gn.Config) error { + ethCfg.InteropMessageRPC = s.supervisor.RPC() + return nil + }) require.NoError(s.t, err) require.NoError(s.t, l2Geth.Node.Start()) s.t.Cleanup(func() { - _ = l2Geth.Close() + s.t.Logf("Closing L2 geth of chain %s", id) + closeErr := l2Geth.Close() + s.t.Logf("Closed L2 geth of chain %s: %v", id, closeErr) }) return l2Geth } @@ -306,7 +332,9 @@ func (s *interopE2ESystem) newNodeForL2( s.t.Cleanup(func() { ctx, cancel := context.WithCancel(context.Background()) cancel() // force-quit + s.t.Logf("Closing op-node of chain %s", id) _ = opNode.Stop(ctx) + s.t.Logf("Closed op-node of chain %s", id) }) return opNode } @@ -390,7 +418,9 @@ func (s *interopE2ESystem) newBatcherForL2( s.t.Cleanup(func() { ctx, cancel := context.WithCancel(context.Background()) cancel() // force-quit + s.t.Logf("Closing batcher of chain %s", id) _ = batcher.Stop(ctx) + s.t.Logf("Closed batcher of chain %s", id) }) return batcher } @@ -471,7 +501,9 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService { s.t.Cleanup(func() { ctx, cancel := context.WithCancel(context.Background()) cancel() // force-quit - _ = super.Stop(ctx) + s.t.Logf("Closing supervisor") + closeErr := super.Stop(ctx) + s.t.Logf("Closed supervisor: %v", closeErr) }) return super } @@ -503,12 +535,23 @@ func (s *interopE2ESystem) prepare(t *testing.T, w worldResourcePaths) { s.beacon, s.l1 = s.prepareL1() s.l2s = s.prepareL2s() + s.prepareContracts() + // add the L2 RPCs to the supervisor now that the L2s are created ctx := context.Background() for _, l2 := range s.l2s { err := s.SupervisorClient().AddL2RPC(ctx, l2.l2Geth.UserRPC().RPC()) require.NoError(s.t, err, "failed to add L2 RPC to supervisor") } + + // Try to close the op-supervisor first + s.t.Cleanup(func() { + ctx, cancel := context.WithCancel(context.Background()) + cancel() // force-quit + s.t.Logf("Closing supervisor") + closeErr := s.supervisor.Stop(ctx) + s.t.Logf("Closed supervisor: %v", closeErr) + }) } // AddUser adds a user to the system by creating a user key for each L2. @@ -552,6 +595,44 @@ func (s *interopE2ESystem) prepareL2s() map[string]l2Set { return l2s } +// prepareContracts prepares contract-bindings for the L2s +func (s *interopE2ESystem) prepareContracts() { + // Add bindings to common contracts for each L2 + l1GethClient := s.L1GethClient() + for id, l2Dep := range s.worldDeployment.L2s { + { + contract, err := inbox.NewInbox(predeploys.CrossL2InboxAddr, s.L2GethClient(id)) + require.NoError(s.t, err) + s.l2s[id].contracts["inbox"] = contract + } + { + contract, err := systemconfig.NewSystemconfig(l2Dep.SystemConfigProxy, l1GethClient) + require.NoError(s.t, err) + s.l2s[id].contracts["systemconfig"] = contract + } + } +} + +func (s *interopE2ESystem) L1GethClient() *ethclient.Client { + if s.l1GethClient != nil { + return s.l1GethClient + } + rpcEndpoint := s.l1.UserRPC() + rpcCl := endpoint.DialRPC( + endpoint.PreferAnyRPC, + rpcEndpoint, + func(v string) *rpc.Client { + logger := testlog.Logger(s.t, log.LevelInfo) + cl, err := dial.DialRPCClientWithTimeout(context.Background(), 30*time.Second, logger, v) + require.NoError(s.t, err, "failed to dial L1 eth node instance") + return cl + }) + nodeClient := ethclient.NewClient(rpcCl) + // register the client so it can be reused + s.l1GethClient = nodeClient + return nodeClient +} + func (s *interopE2ESystem) L2GethClient(id string) *ethclient.Client { // guard: check if the client already exists and return it in that case nodeClient, ok := s.l2GethClients[id] @@ -639,6 +720,64 @@ func (s *interopE2ESystem) SendL2Tx( newApply) } +func (s *interopE2ESystem) ExecuteMessage( + ctx context.Context, + id string, + sender string, + msgIdentifier supervisortypes.Identifier, + target common.Address, + message []byte, +) (*types.Receipt, error) { + secret := s.UserKey(id, sender) + auth, err := bind.NewKeyedTransactorWithChainID(&secret, s.l2s[id].chainID) + + require.NoError(s.t, err) + + auth.GasLimit = uint64(3000_000) + auth.GasPrice = big.NewInt(20_000_000_000) + + contract := s.Contract(id, "inbox").(*inbox.Inbox) + identifier := inbox.Identifier{ + Origin: msgIdentifier.Origin, + BlockNumber: new(big.Int).SetUint64(msgIdentifier.BlockNumber), + LogIndex: new(big.Int).SetUint64(uint64(msgIdentifier.LogIndex)), + Timestamp: new(big.Int).SetUint64(msgIdentifier.Timestamp), + ChainId: msgIdentifier.ChainID.ToBig(), + } + tx, err := contract.InboxTransactor.ExecuteMessage(auth, identifier, target, message) + require.NoError(s.t, err) + s.logger.Info("Executing message", "tx", tx.Hash(), "to", tx.To(), "target", target, "data", hexutil.Bytes(tx.Data())) + return bind.WaitMined(ctx, s.L2GethClient(id), tx) +} + +func (s *interopE2ESystem) AddDependency(id string, dep *big.Int) *types.Receipt { + // There is a note in OPContractsManagerInterop that the proxy-admin is used for now, + // even though it should be a separate dependency-set-manager address. + secret, err := s.hdWallet.Secret(devkeys.ChainOperatorKey{ + ChainID: s.l2s[id].chainID, + Role: devkeys.SystemConfigOwner, + }) + require.NoError(s.t, err) + + auth, err := bind.NewKeyedTransactorWithChainID(secret, s.worldOutput.L1.Genesis.Config.ChainID) + require.NoError(s.t, err) + + balance, err := s.l1GethClient.BalanceAt(context.Background(), crypto.PubkeyToAddress(secret.PublicKey), nil) + require.NoError(s.t, err) + require.False(s.t, balance.Sign() == 0, "system config owner needs a balance") + + auth.GasLimit = uint64(3000000) + auth.GasPrice = big.NewInt(20000000000) + + contract := s.Contract(id, "systemconfig").(*systemconfig.Systemconfig) + tx, err := contract.SystemconfigTransactor.AddDependency(auth, dep) + require.NoError(s.t, err) + + receipt, err := wait.ForReceiptOK(context.Background(), s.L1GethClient(), tx.Hash()) + require.NoError(s.t, err) + return receipt +} + func (s *interopE2ESystem) DeployEmitterContract( id string, sender string, diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index d4328f6932c7..cd824bfa6d31 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -267,6 +267,7 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error { if !su.started.CompareAndSwap(true, false) { return errAlreadyStopped } + su.logger.Info("Closing supervisor backend") // close all processors for id, processor := range su.chainProcessors { su.logger.Info("stopping chain processor", "chainID", id) @@ -308,24 +309,27 @@ func (su *SupervisorBackend) DependencySet() depset.DependencySet { // Query methods // ---------------------------- -func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, logHash common.Hash) (types.SafetyLevel, error) { +func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) { su.mu.RLock() defer su.mu.RUnlock() + logHash := types.PayloadHashToLogHash(payloadHash, identifier.Origin) chainID := identifier.ChainID blockNum := identifier.BlockNumber logIdx := identifier.LogIndex - _, err := su.chainDBs.Check(chainID, blockNum, uint32(logIdx), logHash) + _, err := su.chainDBs.Check(chainID, blockNum, logIdx, logHash) if errors.Is(err, types.ErrFuture) { + su.logger.Debug("Future message", "identifier", identifier, "payloadHash", payloadHash, "err", err) return types.LocalUnsafe, nil } if errors.Is(err, types.ErrConflict) { + su.logger.Debug("Conflicting message", "identifier", identifier, "payloadHash", payloadHash, "err", err) return types.Invalid, nil } if err != nil { return types.Invalid, fmt.Errorf("failed to check log: %w", err) } - return su.chainDBs.Safest(chainID, blockNum, uint32(logIdx)) + return su.chainDBs.Safest(chainID, blockNum, logIdx) } func (su *SupervisorBackend) CheckMessages( @@ -334,12 +338,21 @@ func (su *SupervisorBackend) CheckMessages( su.mu.RLock() defer su.mu.RUnlock() + su.logger.Debug("Checking messages", "count", len(messages), "minSafety", minSafety) + for _, msg := range messages { + su.logger.Debug("Checking message", + "identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String()) safety, err := su.CheckMessage(msg.Identifier, msg.PayloadHash) if err != nil { + su.logger.Error("Check message failed", "err", err, + "identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String()) return fmt.Errorf("failed to check message: %w", err) } if !safety.AtLeastAsSafe(minSafety) { + su.logger.Error("Message is not sufficiently safe", + "safety", safety, "minSafety", minSafety, + "identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String()) return fmt.Errorf("message %v (safety level: %v) does not meet the minimum safety %v", msg.Identifier, safety, diff --git a/op-supervisor/supervisor/backend/db/logs/db.go b/op-supervisor/supervisor/backend/db/logs/db.go index dc98b7472074..5509c5e61df8 100644 --- a/op-supervisor/supervisor/backend/db/logs/db.go +++ b/op-supervisor/supervisor/backend/db/logs/db.go @@ -287,7 +287,7 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ db.log.Trace("Found initiatingEvent", "blockNum", blockNum, "logIdx", logIdx, "hash", evtHash) // Found the requested block and log index, check if the hash matches if evtHash != logHash { - return types.BlockSeal{}, fmt.Errorf("payload hash mismatch: expected %s, got %s", logHash, evtHash) + return types.BlockSeal{}, fmt.Errorf("payload hash mismatch: expected %s, got %s %w", logHash, evtHash, types.ErrConflict) } // Now find the block seal after the log, to identify where the log was included in. err = iter.TraverseConditional(func(state IteratorState) error { diff --git a/op-supervisor/supervisor/backend/processors/log_processor.go b/op-supervisor/supervisor/backend/processors/log_processor.go index 1434d2a60395..e8adb29ce6bf 100644 --- a/op-supervisor/supervisor/backend/processors/log_processor.go +++ b/op-supervisor/supervisor/backend/processors/log_processor.go @@ -77,18 +77,6 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts // The address is hashed into the payload hash to save space in the log storage, // and because they represent paired data. func logToLogHash(l *ethTypes.Log) common.Hash { - payloadHash := crypto.Keccak256(logToMessagePayload(l)) + payloadHash := crypto.Keccak256(types.LogToMessagePayload(l)) return types.PayloadHashToLogHash(common.Hash(payloadHash), l.Address) } - -// logToMessagePayload is the data that is hashed to get the logHash -// it is the concatenation of the log's topics and data -// the implementation is based on the interop messaging spec -func logToMessagePayload(l *ethTypes.Log) []byte { - msg := make([]byte, 0) - for _, topic := range l.Topics { - msg = append(msg, topic.Bytes()...) - } - msg = append(msg, l.Data...) - return msg -} diff --git a/op-supervisor/supervisor/service.go b/op-supervisor/supervisor/service.go index 8bccaf1eb82f..7054c7e0ff55 100644 --- a/op-supervisor/supervisor/service.go +++ b/op-supervisor/supervisor/service.go @@ -184,6 +184,7 @@ func (su *SupervisorService) Start(ctx context.Context) error { func (su *SupervisorService) Stop(ctx context.Context) error { if !su.closing.CompareAndSwap(false, true) { + su.log.Warn("Supervisor is already closing") return nil // already closing } su.log.Info("Stopping JSON-RPC server") diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index b1aab4d081e8..bac2c377bfd7 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -8,6 +8,8 @@ import ( "math/big" "strconv" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" @@ -58,7 +60,7 @@ type Message struct { type Identifier struct { Origin common.Address BlockNumber uint64 - LogIndex uint64 + LogIndex uint32 Timestamp uint64 ChainID ChainID // flat, not a pointer, to make Identifier safe as map key } @@ -88,7 +90,10 @@ func (id *Identifier) UnmarshalJSON(input []byte) error { } id.Origin = dec.Origin id.BlockNumber = uint64(dec.BlockNumber) - id.LogIndex = uint64(dec.LogIndex) + if dec.LogIndex > math.MaxUint32 { + return fmt.Errorf("log index too large: %d", dec.LogIndex) + } + id.LogIndex = uint32(dec.LogIndex) id.Timestamp = uint64(dec.Timestamp) id.ChainID = (ChainID)(dec.ChainID) return nil @@ -190,6 +195,10 @@ func (id ChainID) ToUInt32() (uint32, error) { return uint32(v64), nil } +func (id *ChainID) ToBig() *big.Int { + return (*uint256.Int)(id).ToBig() +} + func (id ChainID) MarshalText() ([]byte, error) { return []byte(id.String()), nil } @@ -264,3 +273,15 @@ func PayloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.H msg = append(msg, payloadHash.Bytes()...) return crypto.Keccak256Hash(msg) } + +// LogToMessagePayload is the data that is hashed to get the payloadHash +// it is the concatenation of the log's topics and data +// the implementation is based on the interop messaging spec +func LogToMessagePayload(l *ethTypes.Log) []byte { + msg := make([]byte, 0) + for _, topic := range l.Topics { + msg = append(msg, topic.Bytes()...) + } + msg = append(msg, l.Data...) + return msg +} diff --git a/op-supervisor/supervisor/types/types_test.go b/op-supervisor/supervisor/types/types_test.go index 50c856bdc794..7e3e0d4843a9 100644 --- a/op-supervisor/supervisor/types/types_test.go +++ b/op-supervisor/supervisor/types/types_test.go @@ -13,7 +13,7 @@ import ( ) func FuzzRoundtripIdentifierJSONMarshal(f *testing.F) { - f.Fuzz(func(t *testing.T, origin []byte, blockNumber uint64, logIndex uint64, timestamp uint64, chainID []byte) { + f.Fuzz(func(t *testing.T, origin []byte, blockNumber uint64, logIndex uint32, timestamp uint64, chainID []byte) { if len(chainID) > 32 { chainID = chainID[:32] } @@ -55,7 +55,19 @@ func TestChainID_String(t *testing.T) { for _, test := range tests { test := test t.Run(test.expected, func(t *testing.T) { - require.Equal(t, test.expected, test.input.String()) + t.Run("String", func(t *testing.T) { + require.Equal(t, test.expected, test.input.String()) + }) + t.Run("MarshalText", func(t *testing.T) { + data, err := test.input.MarshalText() + require.NoError(t, err) + require.Equal(t, test.expected, string(data)) + }) + t.Run("UnmarshalText", func(t *testing.T) { + var id ChainID + require.NoError(t, id.UnmarshalText([]byte(test.expected))) + require.Equal(t, test.input, id) + }) }) } } diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol index 9d541434a397..19de5537b41c 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol @@ -32,12 +32,12 @@ contract OPContractsManagerInterop is OPContractsManager { (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = defaultSystemConfigParams(_selector, _input, _output); - // TODO For now we assume that the dependency manager is the same as the proxy admin owner. + // TODO For now we assume that the dependency manager is the same as system config owner. // This is currently undefined since it's not part of the standard config, so we may need // to update where this value is pulled from in the future. To support a different dependency // manager in this contract without an invasive change of redefining the `Roles` struct, // we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783. - address dependencyManager = address(_input.roles.opChainProxyAdminOwner); + address dependencyManager = address(_input.roles.systemConfigOwner); return abi.encodeWithSelector( _selector, From 3b99809541de1cec181bc886b0c032c2ed6d1e55 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 31 Oct 2024 16:05:51 -0600 Subject: [PATCH 093/451] ci: Parallelize fuzz jobs (#12765) * ci: Parallelize fuzz jobs Parallelize fuzz `make` targets using `xargs`. Previously, they were all running serially which led to long job runtimes. * Use machine * reduce executors to 8 --- .circleci/config.yml | 23 ++++------------------- cannon/Makefile | 32 +++++++++++++++----------------- op-batcher/Makefile | 18 ++++++++++-------- op-chain-ops/Makefile | 10 ++++++---- op-e2e/Makefile | 8 +++++--- op-node/Makefile | 28 +++++++++++++++------------- op-service/Makefile | 18 ++++++++++-------- 7 files changed, 65 insertions(+), 72 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7a5d9e3a5ebb..7430b9b5c9ac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -745,8 +745,8 @@ jobs: description: should load in foundry artifacts type: boolean default: false - docker: - - image: <> + machine: true + resource_class: ethereum-optimism/latitude-1 steps: - checkout - check-changed: @@ -754,23 +754,10 @@ jobs: - attach_workspace: at: "." if: ${{ uses_artifacts }} - - restore_cache: - name: Restore Go modules cache - key: gomod-{{ checksum "go.sum" }} - - restore_cache: - name: Restore Go build cache - keys: - - golang-build-cache-fuzz-golang-{{ checksum "go.sum" }} - - golang-build-cache-fuzz-golang- - run: name: Fuzz command: make fuzz working_directory: "<>" - - save_cache: - name: Save Go build cache - key: golang-build-cache-fuzz-golang-{{ checksum "go.sum" }} - paths: - - "/root/.cache/go-build" go-lint: machine: true @@ -1323,8 +1310,6 @@ workflows: - go-lint - fuzz-golang: name: fuzz-golang-<> - requires: - - go-mod-download on_changes: <> matrix: parameters: @@ -1338,13 +1323,13 @@ workflows: package_name: cannon on_changes: cannon,packages/contracts-bedrock/src/cannon uses_artifacts: true - requires: ["go-mod-download", "contracts-bedrock-build"] + requires: ["contracts-bedrock-build"] - fuzz-golang: name: op-e2e-fuzz package_name: op-e2e on_changes: op-e2e,packages/contracts-bedrock/src uses_artifacts: true - requires: ["go-mod-download", "contracts-bedrock-build"] + requires: ["contracts-bedrock-build"] - go-test: name: go-test-all requires: diff --git a/cannon/Makefile b/cannon/Makefile index 141e7d5be85b..d487485a625b 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -86,23 +86,21 @@ cannon-stf-verify: @docker build --progress plain -f Dockerfile.diff ../ fuzz: - # Common vm tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests - # Single-threaded tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests - # Multi-threaded tests - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests - # 64-bit tests - increased fuzztime for a larger input space - go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests - go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests - go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests + printf "%s\n" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \ + | parallel -j 8 {} .PHONY: \ cannon32-impl \ diff --git a/op-batcher/Makefile b/op-batcher/Makefile index 8e680ec2bec1..22bb7a613861 100644 --- a/op-batcher/Makefile +++ b/op-batcher/Makefile @@ -34,14 +34,16 @@ test: go test -v ./... fuzz: - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelConfig_CheckTimeout ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationZero ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutMaxChannelDuration ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutZeroMaxChannelDuration ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelCloseTimeout ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelZeroCloseTimeout ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowClose ./batcher - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowZeroTimeoutClose ./batcher + printf "%s\n" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelConfig_CheckTimeout ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationZero ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutMaxChannelDuration ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutZeroMaxChannelDuration ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelCloseTimeout ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelZeroCloseTimeout ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowClose ./batcher" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowZeroTimeoutClose ./batcher" \ + | parallel -j 8 {} .PHONY: \ op-batcher \ diff --git a/op-chain-ops/Makefile b/op-chain-ops/Makefile index 99cd1aa80d2c..ad595b65e367 100644 --- a/op-chain-ops/Makefile +++ b/op-chain-ops/Makefile @@ -39,10 +39,12 @@ op-deployer: GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) CGO_ENABLED=0 go build -v $(LDFLAGS) -o ./bin/op-deployer ../op-deployer/cmd/op-deployer/main.go fuzz: - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeWithdrawal ./crossdomain - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeLegacyWithdrawal ./crossdomain - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzAliasing ./crossdomain - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzVersionedNonce ./crossdomain + printf "%s\n" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeWithdrawal ./crossdomain" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeLegacyWithdrawal ./crossdomain" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzAliasing ./crossdomain" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzVersionedNonce ./crossdomain" \ + | parallel -j 8 {} sync-standard-version: diff --git a/op-e2e/Makefile b/op-e2e/Makefile index 7ddbb42528bd..4c808ac45c04 100644 --- a/op-e2e/Makefile +++ b/op-e2e/Makefile @@ -69,6 +69,8 @@ clean: .PHONY: clean fuzz: - go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFjordCostFunction ./opgeth - go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFastLzGethSolidity ./opgeth - go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFastLzCgo ./opgeth + printf "%s\n" \ + "go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFjordCostFunction ./opgeth" \ + "go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFastLzGethSolidity ./opgeth" \ + "go test -run NOTAREALTEST -tags cgo_test -v -fuzztime 10s -fuzz FuzzFastLzCgo ./opgeth" \ + | parallel -j 8 {} diff --git a/op-node/Makefile b/op-node/Makefile index 75b152b2d53e..c1d480d9b71a 100644 --- a/op-node/Makefile +++ b/op-node/Makefile @@ -35,19 +35,21 @@ test: go test -v ./... fuzz: - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoBedrockRoundTrip ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoEcotoneRoundTrip ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoAgainstContract ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzUnmarshallLogEvent ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseFrames ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFrameUnmarshalBinary ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzBatchRoundTrip ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsRoundTrip ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsBadVersion ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataValid ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataBadLength ./rollup/derive - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzRejectCreateBlockBadTimestamp ./rollup/driver - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDecodeDepositTxDataToL1Info ./rollup/driver + printf "%s\n" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoBedrockRoundTrip ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoEcotoneRoundTrip ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoAgainstContract ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzUnmarshallLogEvent ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseFrames ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFrameUnmarshalBinary ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzBatchRoundTrip ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsRoundTrip ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsBadVersion ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataValid ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataBadLength ./rollup/derive" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzRejectCreateBlockBadTimestamp ./rollup/driver" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDecodeDepositTxDataToL1Info ./rollup/driver" \ + | parallel -j 8 {} generate-mocks: go generate ./... diff --git a/op-service/Makefile b/op-service/Makefile index 0978529f9b8d..f57c0b6b53a4 100644 --- a/op-service/Makefile +++ b/op-service/Makefile @@ -10,14 +10,16 @@ generate-mocks: go generate ./... fuzz: - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadUnmarshal ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV1 ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV2 ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV3 ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzOBP01 ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeDecodeBlob ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDetectNonBijectivity ./eth - go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeScalar ./eth + printf "%s\n" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadUnmarshal ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV1 ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV2 ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshalV3 ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzOBP01 ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeDecodeBlob ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDetectNonBijectivity ./eth" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeScalar ./eth" \ + | parallel -j 8 {} .PHONY: \ test \ From 6f2058c4603c389c6d30d3d8f541508ee1eccf36 Mon Sep 17 00:00:00 2001 From: mbaxter Date: Thu, 31 Oct 2024 18:31:05 -0400 Subject: [PATCH 094/451] cannon: Add noop stat syscall to MIPS64.sol (#12766) * cannon: Add stat syscall to MIP64 contracts * cannon: Run semver lock task --- packages/contracts-bedrock/semver-lock.json | 4 ++-- packages/contracts-bedrock/src/cannon/MIPS64.sol | 6 ++++-- .../src/cannon/libraries/MIPS64Syscalls.sol | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 502b9d0ba6f9..1cee134ca198 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -148,8 +148,8 @@ "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" }, "src/cannon/MIPS64.sol": { - "initCodeHash": "0x47c2bdd1e6fbb4941caa20a2ba5c2a66de198a8c7b540a9d4a0d84dbe8d3bfea", - "sourceCodeHash": "0xdb7f8a92ed552a2720f5fe3c0a32e4069026f0b23933145493ead88403206814" + "initCodeHash": "0x5094e3f488de838cba1422b508a899444ee27905abc9c0b97961d4b8b7392289", + "sourceCodeHash": "0xdc2393d2073348218a9c6e169c2e7f687590e237773b8fde53fae9cf84395978" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS64.sol b/packages/contracts-bedrock/src/cannon/MIPS64.sol index dcc2a6ec2190..8c85a26acd43 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS64.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS64.sol @@ -64,8 +64,8 @@ contract MIPS64 is ISemver { } /// @notice The semantic version of the MIPS64 contract. - /// @custom:semver 1.0.0-beta.1 - string public constant version = "1.0.0-beta.1"; + /// @custom:semver 1.0.0-beta.2 + string public constant version = "1.0.0-beta.2"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -598,6 +598,8 @@ contract MIPS64 is ISemver { // ignored } else if (syscall_no == sys.SYS_PREAD64) { // ignored + } else if (syscall_no == sys.SYS_STAT) { + // ignored } else if (syscall_no == sys.SYS_FSTAT) { // ignored } else if (syscall_no == sys.SYS_OPENAT) { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol index d03f04f08620..10954c64748e 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol @@ -58,6 +58,7 @@ library MIPS64Syscalls { uint32 internal constant SYS_PRLIMIT64 = 5297; uint32 internal constant SYS_CLOSE = 5003; uint32 internal constant SYS_PREAD64 = 5016; + uint32 internal constant SYS_STAT = 5004; uint32 internal constant SYS_FSTAT = 5005; //uint32 internal constant SYS_FSTAT64 = 0xFFFFFFFF; // UndefinedSysNr - not supported by MIPS64 uint32 internal constant SYS_OPENAT = 5247; From 1b4fda30ac55a9e234321974492caa156e1a954a Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 31 Oct 2024 18:55:51 -0400 Subject: [PATCH 095/451] fix: Make public DeployUtils lib functions internal (#12759) --- .../contracts-bedrock/scripts/libraries/DeployUtils.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol index 08bfd99f0131..597e5b100fc3 100644 --- a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol +++ b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol @@ -279,7 +279,10 @@ library DeployUtils { /// @notice Builds an L1ChugSplashProxy with a dummy implementation. /// @param _proxyImplName Name of the implementation contract. - function buildL1ChugSplashProxyWithImpl(string memory _proxyImplName) public returns (IL1ChugSplashProxy proxy_) { + function buildL1ChugSplashProxyWithImpl(string memory _proxyImplName) + internal + returns (IL1ChugSplashProxy proxy_) + { proxy_ = IL1ChugSplashProxy( create1({ _name: "L1ChugSplashProxy", @@ -299,7 +302,7 @@ library DeployUtils { IAddressManager _addressManager, string memory _proxyImplName ) - public + internal returns (IResolvedDelegateProxy proxy_) { proxy_ = IResolvedDelegateProxy( @@ -316,7 +319,7 @@ library DeployUtils { } /// @notice Builds an AddressManager contract. - function buildAddressManager() public returns (IAddressManager addressManager_) { + function buildAddressManager() internal returns (IAddressManager addressManager_) { addressManager_ = IAddressManager( create1({ _name: "AddressManager", From fde52b490211bd9361317167b3efb58ed92f31ee Mon Sep 17 00:00:00 2001 From: mbaxter Date: Fri, 1 Nov 2024 01:45:07 -0400 Subject: [PATCH 096/451] cannon: Enable 64-bit tests (#12729) * cannon: Set up fuzz tests to run across 32- and 64-bit VMs Update preimage read fuzz test to work on 64-bit vms * cannon: Run both 32- and 64-bit Cannon tests in CI * cannon: Separate log and coverage files for 32- and 64-bit tests * cannon: Clean up evm validation code * cannon: Move skip utility to testutil * cannon: Make TestState_EncodeWitness 64-bit compatible * cannon: Skip failing 64-bit tests * cannon: Update TestEVM_EmptyThreadStacks to work across 32- and 64-bit vms * cannon: Update version state tests, add build tags * cannon: Fix code coverage upload paths * cannon: Fix code coverage file extensions * cannon: Run 32/64-bit tests using a matrix * cannon: Try to set better name for arch-specific cannon tests * cannon: Use generated job names in bedrock requirements list * cannon: Add comment on new validator util --------- Co-authored-by: Matthew Slipper --- .circleci/config.yml | 41 +++++--- cannon/Makefile | 27 ++++-- cannon/mipsevm/multithreaded/state_test.go | 18 ++-- .../singlethreaded/instrumented_test.go | 3 + cannon/mipsevm/singlethreaded/state_test.go | 3 + cannon/mipsevm/tests/evm_common_test.go | 51 +++------- .../mipsevm/tests/evm_multithreaded_test.go | 31 +++--- .../mipsevm/tests/evm_singlethreaded_test.go | 3 + cannon/mipsevm/tests/fuzz_evm_common_test.go | 96 ++++++++++++++++--- .../tests/fuzz_evm_multithreaded_test.go | 11 ++- .../tests/fuzz_evm_singlethreaded_test.go | 11 +-- cannon/mipsevm/testutil/mips.go | 36 +++++-- cannon/mipsevm/testutil/vmtests.go | 7 ++ cannon/mipsevm/versions/state64_test.go | 64 +++++++++++++ cannon/mipsevm/versions/state_test.go | 10 +- 15 files changed, 300 insertions(+), 112 deletions(-) create mode 100644 cannon/mipsevm/versions/state64_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 7430b9b5c9ac..3a089dd72bb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -162,9 +162,9 @@ jobs: description: Whether to notify on failure type: boolean default: false - mips64: - type: boolean - default: false + mips_word_size: + type: integer + default: 32 steps: - checkout - check-changed: @@ -187,28 +187,34 @@ jobs: working_directory: cannon - when: condition: - not: <> + equal: [32, <>] steps: - run: name: Cannon Go 32-bit tests command: | export SKIP_SLOW_TESTS=<> - gotestsum --format=testname --junitfile=../tmp/test-results/cannon.xml --jsonfile=../tmp/testlogs/log.json \ - -- -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... + gotestsum --format=testname --junitfile=../tmp/test-results/cannon-32.xml --jsonfile=../tmp/testlogs/log-32.json \ + -- -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-32.out ./... + working_directory: cannon + - run: + name: Upload Cannon coverage + command: codecov --verbose --clean --flags cannon-go-tests-32 -f ./coverage-32.out working_directory: cannon - when: - condition: <> + condition: + equal: [64, <>] steps: - run: name: Cannon Go 64-bit tests command: | export SKIP_SLOW_TESTS=<> - gotestsum --format=testname --junitfile=../tmp/test-results/cannon.xml --jsonfile=../tmp/testlogs/log.json \ - -- --tags=cannon64 -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... + gotestsum --format=testname --junitfile=../tmp/test-results/cannon-64.xml --jsonfile=../tmp/testlogs/log-64.json \ + -- --tags=cannon64 -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-64.out ./... + working_directory: cannon + - run: + name: Upload Cannon coverage + command: codecov --verbose --clean --flags cannon-go-tests-64 -f ./coverage-64.out working_directory: cannon - - run: - name: upload Cannon coverage - command: codecov --verbose --clean --flags cannon-go-tests - store_test_results: path: ./tmp/test-results - store_artifacts: @@ -1365,7 +1371,8 @@ workflows: - go-mod-download - go-lint - cannon-build-test-vectors - - cannon-go-lint-and-test + - cannon-go-lint-and-test-32-bit + - cannon-go-lint-and-test-64-bit - check-generated-mocks-op-node - check-generated-mocks-op-service - go-mod-download @@ -1403,10 +1410,14 @@ workflows: - check-generated-mocks-op-node - check-generated-mocks-op-service - cannon-go-lint-and-test: + name: cannon-go-lint-and-test-<>-bit requires: - contracts-bedrock-build skip_slow_tests: true notify: true + matrix: + parameters: + mips_word_size: [ 32, 64 ] - cannon-build-test-vectors - todo-issues: name: todo-issues-check @@ -1605,10 +1616,14 @@ workflows: - contracts-bedrock-build: skip_pattern: test - cannon-go-lint-and-test: + name: cannon-go-lint-and-test-<>-bit requires: - contracts-bedrock-build context: - slack + matrix: + parameters: + mips_word_size: [ 32, 64 ] scheduled-docker-publish: when: diff --git a/cannon/Makefile b/cannon/Makefile index d487485a625b..f71ffa24d200 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -87,19 +87,28 @@ cannon-stf-verify: fuzz: printf "%s\n" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite32 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallBrk64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallMmap64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallExitGroup64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallFcntl64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintRead64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintWrite64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallCloneMT64 ./mipsevm/tests" \ | parallel -j 8 {} .PHONY: \ diff --git a/cannon/mipsevm/multithreaded/state_test.go b/cannon/mipsevm/multithreaded/state_test.go index 3f8954c63625..a4ed646b5325 100644 --- a/cannon/mipsevm/multithreaded/state_test.go +++ b/cannon/mipsevm/multithreaded/state_test.go @@ -25,6 +25,10 @@ func setWitnessField(witness StateWitness, fieldOffset int, fieldData []byte) { copy(witness[start:end], fieldData) } +func setWitnessWord(witness StateWitness, fieldOffset int, value arch.Word) { + arch.ByteOrderWord.PutWord(witness[fieldOffset:], value) +} + // Run through all permutations of `exited` / `exitCode` and ensure that the // correct witness, state hash, and VM Status is produced. func TestState_EncodeWitness(t *testing.T) { @@ -70,27 +74,27 @@ func TestState_EncodeWitness(t *testing.T) { expectedWitness := make(StateWitness, STATE_WITNESS_SIZE) setWitnessField(expectedWitness, MEMROOT_WITNESS_OFFSET, memRoot[:]) setWitnessField(expectedWitness, PREIMAGE_KEY_WITNESS_OFFSET, preimageKey[:]) - setWitnessField(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, []byte{0, 0, 0, byte(preimageOffset)}) - setWitnessField(expectedWitness, HEAP_WITNESS_OFFSET, []byte{0, 0, 0, byte(heap)}) + setWitnessWord(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, preimageOffset) + setWitnessWord(expectedWitness, HEAP_WITNESS_OFFSET, heap) setWitnessField(expectedWitness, LL_RESERVATION_ACTIVE_OFFSET, []byte{1}) - setWitnessField(expectedWitness, LL_ADDRESS_OFFSET, []byte{0, 0, 0, byte(llAddress)}) - setWitnessField(expectedWitness, LL_OWNER_THREAD_OFFSET, []byte{0, 0, 0, byte(llThreadOwner)}) + setWitnessWord(expectedWitness, LL_ADDRESS_OFFSET, llAddress) + setWitnessWord(expectedWitness, LL_OWNER_THREAD_OFFSET, llThreadOwner) setWitnessField(expectedWitness, EXITCODE_WITNESS_OFFSET, []byte{c.exitCode}) if c.exited { setWitnessField(expectedWitness, EXITED_WITNESS_OFFSET, []byte{1}) } setWitnessField(expectedWitness, STEP_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(step)}) setWitnessField(expectedWitness, STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(stepsSinceContextSwitch)}) - setWitnessField(expectedWitness, WAKEUP_WITNESS_OFFSET, []byte{0xFF, 0xFF, 0xFF, 0xFF}) + setWitnessWord(expectedWitness, WAKEUP_WITNESS_OFFSET, ^arch.Word(0)) setWitnessField(expectedWitness, TRAVERSE_RIGHT_WITNESS_OFFSET, []byte{0}) setWitnessField(expectedWitness, LEFT_THREADS_ROOT_WITNESS_OFFSET, leftStackRoot[:]) setWitnessField(expectedWitness, RIGHT_THREADS_ROOT_WITNESS_OFFSET, rightStackRoot[:]) - setWitnessField(expectedWitness, THREAD_ID_WITNESS_OFFSET, []byte{0, 0, 0, 1}) + setWitnessWord(expectedWitness, THREAD_ID_WITNESS_OFFSET, 1) // Validate witness actualWitness, actualStateHash := state.EncodeWitness() require.Equal(t, len(actualWitness), STATE_WITNESS_SIZE, "Incorrect witness size") - require.EqualValues(t, expectedWitness[:], actualWitness[:], "Incorrect witness") + require.EqualValues(t, expectedWitness[:], actualWitness[:], "Incorrect witness: \n\tactual = \t0x%x \n\texpected = \t0x%x", actualWitness, expectedWitness) // Validate witness hash expectedStateHash := crypto.Keccak256Hash(actualWitness) expectedStateHash[0] = mipsevm.VmStatus(c.exited, c.exitCode) diff --git a/cannon/mipsevm/singlethreaded/instrumented_test.go b/cannon/mipsevm/singlethreaded/instrumented_test.go index d4c190d9cd91..f1dfc8fae4db 100644 --- a/cannon/mipsevm/singlethreaded/instrumented_test.go +++ b/cannon/mipsevm/singlethreaded/instrumented_test.go @@ -1,3 +1,6 @@ +//go:build !cannon64 +// +build !cannon64 + package singlethreaded import ( diff --git a/cannon/mipsevm/singlethreaded/state_test.go b/cannon/mipsevm/singlethreaded/state_test.go index e0639c3dbeec..bfab4dd27988 100644 --- a/cannon/mipsevm/singlethreaded/state_test.go +++ b/cannon/mipsevm/singlethreaded/state_test.go @@ -1,3 +1,6 @@ +//go:build !cannon64 +// +build !cannon64 + package singlethreaded import ( diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index be316749cd61..2aa1def61ca7 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/tracing" "github.com/stretchr/testify/require" @@ -37,6 +36,7 @@ func TestEVM(t *testing.T) { for _, f := range testFiles { testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) for _, skipped := range skipped { if f.Name() == skipped { t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name()) @@ -47,10 +47,7 @@ func TestEVM(t *testing.T) { // Short-circuit early for exit_group.bin exitGroup := f.Name() == "exit_group.bin" expectPanic := strings.HasSuffix(f.Name(), "panic.bin") - - evm := testutil.NewMIPSEVM(c.Contracts) - evm.SetLocalOracle(oracle) - testutil.LogStepFailureAtCleanup(t, evm) + validator := testutil.NewEvmValidator(t, c.StateHashFn, c.Contracts, testutil.WithLocalOracle(oracle)) fn := path.Join("open_mips_tests/test/bin", f.Name()) programMem, err := os.ReadFile(fn) @@ -88,12 +85,7 @@ func TestEVM(t *testing.T) { stepWitness, err := goVm.Step(true) require.NoError(t, err) - evmPost := evm.Step(t, stepWitness, curStep, c.StateHashFn) - // verify the post-state matches. - // TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison. - goPost, _ := goVm.GetState().EncodeWitness() - require.Equalf(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM at step %d", state.GetStep()) + validator.ValidateEVM(t, stepWitness, curStep, goVm) } if exitGroup { require.NotEqual(t, arch.Word(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end") @@ -215,6 +207,7 @@ func TestEVMSingleStep_Operators(t *testing.T) { for i, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) state := goVm.GetState() var insn uint32 @@ -316,6 +309,7 @@ func TestEVMSingleStep_LoadStore(t *testing.T) { for _, v := range versions { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) addr := tt.base + Word(tt.imm) effAddr := arch.AddressMask & addr @@ -490,6 +484,7 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { for i, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) state := goVm.GetState() var insn uint32 @@ -864,6 +859,7 @@ func TestEVMFault(t *testing.T) { for _, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC)) state := goVm.GetState() testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) @@ -886,8 +882,7 @@ func TestHelloEVM(t *testing.T) { v := v t.Run(v.Name, func(t *testing.T) { t.Parallel() - evm := testutil.NewMIPSEVM(v.Contracts) - testutil.LogStepFailureAtCleanup(t, evm) + validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts) var stdOutBuf, stdErrBuf bytes.Buffer elfFile := testutil.ProgramPath("hello") @@ -907,12 +902,7 @@ func TestHelloEVM(t *testing.T) { stepWitness, err := goVm.Step(true) require.NoError(t, err) - evmPost := evm.Step(t, stepWitness, step, v.StateHashFn) - // verify the post-state matches. - // TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison. - goPost, _ := goVm.GetState().EncodeWitness() - require.Equalf(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM. insn: %x", insn) + validator.ValidateEVM(t, stepWitness, step, goVm) } end := time.Now() delta := end.Sub(start) @@ -935,9 +925,7 @@ func TestClaimEVM(t *testing.T) { v := v t.Run(v.Name, func(t *testing.T) { t.Parallel() - evm := testutil.NewMIPSEVM(v.Contracts) - testutil.LogStepFailureAtCleanup(t, evm) - + validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts) oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t) var stdOutBuf, stdErrBuf bytes.Buffer @@ -958,12 +946,7 @@ func TestClaimEVM(t *testing.T) { stepWitness, err := goVm.Step(true) require.NoError(t, err) - - evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn) - - goPost, _ := goVm.GetState().EncodeWitness() - require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM") + validator.ValidateEVM(t, stepWitness, curStep, goVm) } require.True(t, state.GetExited(), "must complete program") @@ -983,8 +966,7 @@ func TestEntryEVM(t *testing.T) { v := v t.Run(v.Name, func(t *testing.T) { t.Parallel() - evm := testutil.NewMIPSEVM(v.Contracts) - testutil.LogStepFailureAtCleanup(t, evm) + validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts) var stdOutBuf, stdErrBuf bytes.Buffer elfFile := testutil.ProgramPath("entry") @@ -992,7 +974,7 @@ func TestEntryEVM(t *testing.T) { state := goVm.GetState() start := time.Now() - for i := 0; i < 400_000; i++ { + for i := 0; i < 500_000; i++ { curStep := goVm.GetState().GetStep() if goVm.GetState().GetExited() { break @@ -1004,11 +986,7 @@ func TestEntryEVM(t *testing.T) { stepWitness, err := goVm.Step(true) require.NoError(t, err) - evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn) - // verify the post-state matches. - goPost, _ := goVm.GetState().EncodeWitness() - require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM") + validator.ValidateEVM(t, stepWitness, curStep, goVm) } end := time.Now() delta := end.Sub(start) @@ -1090,6 +1068,7 @@ func TestEVMSingleStepBranch(t *testing.T) { for i, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) state := goVm.GetState() const rsReg = 8 // t0 diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 1e1704558abd..ba858585980b 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -8,7 +8,6 @@ import ( "slices" "testing" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -243,6 +242,7 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { for _, v := range llVariations { tName := fmt.Sprintf("%v (%v)", c.name, v.name) t.Run(tName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) effAddr := arch.AddressMask & c.addr preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() oracle := testutil.StaticOracle(t, preimageValue) @@ -339,6 +339,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { for _, v := range llVariations { tName := fmt.Sprintf("%v (%v)", c.name, v.name) t.Run(tName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08)) step := state.GetStep() @@ -409,31 +410,25 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { var err error var stepWitness *mipsevm.StepWitness - us := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil, nil) + goVm := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil, nil) if !c.valid { // The VM should exit - stepWitness, err = us.Step(true) + stepWitness, err = goVm.Step(true) require.NoError(t, err) require.Equal(t, curStep+1, state.GetStep()) - require.Equal(t, true, us.GetState().GetExited()) - require.Equal(t, uint8(mipsevm.VMStatusPanic), us.GetState().GetExitCode()) + require.Equal(t, true, goVm.GetState().GetExited()) + require.Equal(t, uint8(mipsevm.VMStatusPanic), goVm.GetState().GetExitCode()) require.Equal(t, 1, state.ThreadCount()) } else { - stepWitness, err = us.Step(true) + stepWitness, err = goVm.Step(true) require.NoError(t, err) require.Equal(t, curStep+1, state.GetStep()) - require.Equal(t, false, us.GetState().GetExited()) - require.Equal(t, uint8(0), us.GetState().GetExitCode()) + require.Equal(t, false, goVm.GetState().GetExited()) + require.Equal(t, uint8(0), goVm.GetState().GetExitCode()) require.Equal(t, 2, state.ThreadCount()) } - evm := testutil.NewMIPSEVM(contracts) - testutil.LogStepFailureAtCleanup(t, evm) - - evmPost := evm.Step(t, stepWitness, curStep, multithreaded.GetStateHashFn()) - goPost, _ := us.GetState().EncodeWitness() - require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM") + testutil.ValidateEVM(t, stepWitness, curStep, goVm, multithreaded.GetStateHashFn(), contracts) }) } } @@ -660,6 +655,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { for i, c := range cases { t.Run(c.name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm, state, contracts := setup(t, i*1234, nil) step := state.GetStep() @@ -729,6 +725,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { for i, c := range cases { t.Run(c.name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm, state, contracts := setup(t, i*1122, nil) mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount) step := state.Step @@ -1109,6 +1106,7 @@ var NoopSyscalls = map[string]uint32{ func TestEVM_NoopSyscall(t *testing.T) { for noopName, noopVal := range NoopSyscalls { t.Run(noopName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm, state, contracts := setup(t, int(noopVal), nil) testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) @@ -1155,6 +1153,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) { i := i syscallNum := syscallNum t.Run(testName, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) t.Parallel() goVm, state, contracts := setup(t, i*3434, nil) // Setup basic getThreadId syscall instruction @@ -1196,7 +1195,7 @@ func TestEVM_EmptyThreadStacks(t *testing.T) { require.PanicsWithValue(t, "Active thread stack is empty", func() { _, _ = goVm.Step(false) }) - errorMessage := "MIPS2: active thread stack is empty" + errorMessage := "active thread stack is empty" testutil.AssertEVMReverts(t, state, contracts, tracer, proofCase.Proof, testutil.CreateErrorStringMatcher(errorMessage)) }) } diff --git a/cannon/mipsevm/tests/evm_singlethreaded_test.go b/cannon/mipsevm/tests/evm_singlethreaded_test.go index f13ac5a68fa8..56c5e31c3463 100644 --- a/cannon/mipsevm/tests/evm_singlethreaded_test.go +++ b/cannon/mipsevm/tests/evm_singlethreaded_test.go @@ -1,3 +1,6 @@ +//go:build !cannon64 +// +build !cannon64 + package tests import ( diff --git a/cannon/mipsevm/tests/fuzz_evm_common_test.go b/cannon/mipsevm/tests/fuzz_evm_common_test.go index d1d10fbeb3cb..c58e1421a9e1 100644 --- a/cannon/mipsevm/tests/fuzz_evm_common_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_common_test.go @@ -19,11 +19,20 @@ import ( const syscallInsn = uint32(0x00_00_00_0c) -func FuzzStateSyscallBrk(f *testing.F) { +func FuzzStateSyscallBrk32(f *testing.F) { + doFuzzStateSyscallBrk(f) +} + +func FuzzStateSyscallBrk64(f *testing.F) { + doFuzzStateSyscallBrk(f) +} + +func doFuzzStateSyscallBrk(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() state.GetRegistersRef()[2] = arch.SysBrk @@ -48,7 +57,15 @@ func FuzzStateSyscallBrk(f *testing.F) { }) } -func FuzzStateSyscallMmap(f *testing.F) { +func FuzzStateSyscallMmap32(f *testing.F) { + doFuzzStateSyscallMmap(f) +} + +func FuzzStateSyscallMmap64(f *testing.F) { + doFuzzStateSyscallMmap(f) +} + +func doFuzzStateSyscallMmap(f *testing.F) { // Add special cases for large memory allocation f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2)) @@ -59,6 +76,7 @@ func FuzzStateSyscallMmap(f *testing.F) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed), testutil.WithHeap(heap)) state := goVm.GetState() @@ -103,11 +121,20 @@ func FuzzStateSyscallMmap(f *testing.F) { }) } -func FuzzStateSyscallExitGroup(f *testing.F) { +func FuzzStateSyscallExitGroup32(f *testing.F) { + doFuzzStateSyscallExitGroup(f) +} + +func FuzzStateSyscallExitGroup64(f *testing.F) { + doFuzzStateSyscallExitGroup(f) +} + +func doFuzzStateSyscallExitGroup(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() @@ -132,11 +159,20 @@ func FuzzStateSyscallExitGroup(f *testing.F) { }) } -func FuzzStateSyscallFcntl(f *testing.F) { +func FuzzStateSyscallFcntl32(f *testing.F) { + doFuzzStateSyscallFcntl(f) +} + +func FuzzStateSyscallFcntl64(f *testing.F) { + doFuzzStateSyscallFcntl(f) +} + +func doFuzzStateSyscallFcntl(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() @@ -188,11 +224,20 @@ func FuzzStateSyscallFcntl(f *testing.F) { }) } -func FuzzStateHintRead(f *testing.F) { +func FuzzStateHintRead32(f *testing.F) { + doFuzzStateHintRead(f) +} + +func FuzzStateHintRead64(f *testing.F) { + doFuzzStateHintRead(f) +} + +func doFuzzStateHintRead(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) preimageData := []byte("hello world") preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() oracle := testutil.StaticOracle(t, preimageData) // only used for hinting @@ -225,14 +270,22 @@ func FuzzStateHintRead(f *testing.F) { }) } -func FuzzStatePreimageRead(f *testing.F) { +func FuzzStatePreimageRead32(f *testing.F) { + doFuzzStatePreimageRead(f) +} + +func FuzzStatePreimageRead64(f *testing.F) { + doFuzzStatePreimageRead(f) +} + +func doFuzzStatePreimageRead(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { effAddr := addr & arch.AddressMask pc = pc & arch.AddressMask - preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF} + preexistingMemoryVal := ^arch.Word(0) preimageValue := []byte("hello world") preimageData := testutil.AddPreimageLengthPrefix(preimageValue) if preimageOffset >= Word(len(preimageData)) || pc == effAddr { @@ -249,11 +302,11 @@ func FuzzStatePreimageRead(f *testing.F) { state.GetRegistersRef()[5] = addr state.GetRegistersRef()[6] = count testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) - state.GetMemory().SetWord(effAddr, arch.ByteOrderWord.Word(preexistingMemoryVal[:])) + state.GetMemory().SetWord(effAddr, preexistingMemoryVal) step := state.GetStep() alignment := addr & arch.ExtMask - writeLen := 4 - alignment + writeLen := arch.WordSizeBytes - alignment if count < writeLen { writeLen = count } @@ -272,7 +325,8 @@ func FuzzStatePreimageRead(f *testing.F) { expected.PreimageOffset += writeLen if writeLen > 0 { // Expect a memory write - expectedMemory := preexistingMemoryVal + var expectedMemory []byte + expectedMemory = arch.ByteOrderWord.AppendWord(expectedMemory, preexistingMemoryVal) copy(expectedMemory[alignment:], preimageData[preimageOffset:preimageOffset+writeLen]) expected.ExpectMemoryWriteWord(effAddr, arch.ByteOrderWord.Word(expectedMemory[:])) } @@ -288,11 +342,20 @@ func FuzzStatePreimageRead(f *testing.F) { }) } -func FuzzStateHintWrite(f *testing.F) { +func FuzzStateHintWrite32(f *testing.F) { + doFuzzStateHintWrite(f) +} + +func FuzzStateHintWrite64(f *testing.F) { + doFuzzStateHintWrite(f) +} + +func doFuzzStateHintWrite(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) // Make sure pc does not overlap with hint data in memory pc := Word(0) if addr <= 8 { @@ -370,11 +433,20 @@ func FuzzStateHintWrite(f *testing.F) { }) } -func FuzzStatePreimageWrite(f *testing.F) { +func FuzzStatePreimageWrite32(f *testing.F) { + doFuzzStatePreimageWrite(f) +} + +func FuzzStatePreimageWrite64(f *testing.F) { + doFuzzStatePreimageWrite(f) +} + +func doFuzzStatePreimageWrite(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { + testutil.TemporarilySkip64BitTests(t) // Make sure pc does not overlap with preimage data in memory pc := Word(0) if addr <= 8 { diff --git a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go index 49f45a4a5f2c..9d4bb46a4271 100644 --- a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go @@ -13,9 +13,18 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func FuzzStateSyscallCloneMT(f *testing.F) { +func FuzzStateSyscallCloneMT32(f *testing.F) { + doFuzzStateSyscallCloneMT(f) +} + +func FuzzStateSyscallCloneMT64(f *testing.F) { + doFuzzStateSyscallCloneMT(f) +} + +func doFuzzStateSyscallCloneMT(f *testing.F) { v := GetMultiThreadedTestCase(f) f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) { + testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := mttestutil.GetMtState(t, goVm) // Update existing threads to avoid collision with nextThreadId diff --git a/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go index 4c6542269a72..375e1fec1832 100644 --- a/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_singlethreaded_test.go @@ -1,10 +1,12 @@ +//go:build !cannon64 +// +build !cannon64 + package tests import ( "os" "testing" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" @@ -32,11 +34,6 @@ func FuzzStateSyscallCloneST(f *testing.F) { require.False(t, stepWitness.HasPreimage()) expected.Validate(t, state) - - evm := testutil.NewMIPSEVM(v.Contracts) - evmPost := evm.Step(t, stepWitness, step, v.StateHashFn) - goPost, _ := goVm.GetState().EncodeWitness() - require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), - "mipsevm produced different state than EVM") + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) }) } diff --git a/cannon/mipsevm/testutil/mips.go b/cannon/mipsevm/testutil/mips.go index 9ba37cb30e2a..0d5322c6fb6e 100644 --- a/cannon/mipsevm/testutil/mips.go +++ b/cannon/mipsevm/testutil/mips.go @@ -40,7 +40,7 @@ type MIPSEVM struct { lastPreimageOracleInput []byte } -func NewMIPSEVM(contracts *ContractMetadata, opts ...evmOption) *MIPSEVM { +func newMIPSEVM(contracts *ContractMetadata, opts ...evmOption) *MIPSEVM { env, evmState := NewEVMEnv(contracts) sender := vm.AccountRef{0x13, 0x37} startingGas := uint64(maxStepGas) @@ -65,6 +65,12 @@ func WithTracingHooks(tracer *tracing.Hooks) evmOption { } } +func WithLocalOracle(oracle mipsevm.PreimageOracle) evmOption { + return func(evm *MIPSEVM) { + evm.SetLocalOracle(oracle) + } +} + func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) { m.env.Config.Tracer = tracer } @@ -192,17 +198,35 @@ func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) { }) } -// ValidateEVM runs a single evm step and validates against an FPVM poststate -func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) { - evm := NewMIPSEVM(contracts, opts...) +type EvmValidator struct { + evm *MIPSEVM + hashFn mipsevm.HashFn +} + +// NewEvmValidator creates a validator that can be run repeatedly across multiple steps +func NewEvmValidator(t *testing.T, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) *EvmValidator { + evm := newMIPSEVM(contracts, opts...) LogStepFailureAtCleanup(t, evm) - evmPost := evm.Step(t, stepWitness, step, hashFn) + return &EvmValidator{ + evm: evm, + hashFn: hashFn, + } +} + +func (v *EvmValidator) ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM) { + evmPost := v.evm.Step(t, stepWitness, step, v.hashFn) goPost, _ := goVm.GetState().EncodeWitness() require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), "mipsevm produced different state than EVM") } +// ValidateEVM runs a single evm step and validates against an FPVM poststate +func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) { + validator := NewEvmValidator(t, hashFn, contracts) + validator.ValidateEVM(t, stepWitness, step, goVm) +} + type ErrMatcher func(*testing.T, []byte) func CreateNoopErrorMatcher() ErrMatcher { @@ -250,7 +274,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract } func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, opts ...evmOption) { - evm := NewMIPSEVM(contracts, opts...) + evm := newMIPSEVM(contracts, opts...) LogStepFailureAtCleanup(t, evm) evm.assertPreimageOracleReverts(t, preimageKey, preimageValue, preimageOffset) diff --git a/cannon/mipsevm/testutil/vmtests.go b/cannon/mipsevm/testutil/vmtests.go index 333720a4ec02..b16550e446c7 100644 --- a/cannon/mipsevm/testutil/vmtests.go +++ b/cannon/mipsevm/testutil/vmtests.go @@ -141,3 +141,10 @@ func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.Create require.Equal(t, expectedStdOut, stdOutBuf.String(), "stdout") require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr") } + +func TemporarilySkip64BitTests(t *testing.T) { + if !arch.IsMips32 { + // TODO(#12598) Update and enable these tests + t.Skip("Skipping 64-bit test") + } +} diff --git a/cannon/mipsevm/versions/state64_test.go b/cannon/mipsevm/versions/state64_test.go new file mode 100644 index 000000000000..ddffd7e618a5 --- /dev/null +++ b/cannon/mipsevm/versions/state64_test.go @@ -0,0 +1,64 @@ +//go:build cannon64 +// +build cannon64 + +package versions + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" + "github.com/ethereum-optimism/optimism/op-service/serialize" +) + +func TestNewFromState(t *testing.T) { + t.Run("multithreaded64", func(t *testing.T) { + actual, err := NewFromState(multithreaded.CreateEmptyState()) + require.NoError(t, err) + require.IsType(t, &multithreaded.State{}, actual.FPVMState) + require.Equal(t, VersionMultiThreaded64, actual.Version) + }) +} + +func TestLoadStateFromFile(t *testing.T) { + t.Run("Multithreaded64FromBinary", func(t *testing.T) { + expected, err := NewFromState(multithreaded.CreateEmptyState()) + require.NoError(t, err) + + path := writeToFile(t, "state.bin.gz", expected) + actual, err := LoadStateFromFile(path) + require.NoError(t, err) + require.Equal(t, expected, actual) + }) +} + +func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { + tests := []struct { + version StateVersion + createState func() mipsevm.FPVMState + }{ + {VersionMultiThreaded64, func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }}, + } + for _, test := range tests { + test := test + t.Run(test.version.String(), func(t *testing.T) { + state, err := NewFromState(test.createState()) + require.NoError(t, err) + + dir := t.TempDir() + path := filepath.Join(dir, "test.json") + err = serialize.Write(path, state, 0o644) + require.ErrorIs(t, err, ErrJsonNotSupported) + }) + } +} + +func writeToFile(t *testing.T, filename string, data serialize.Serializable) string { + dir := t.TempDir() + path := filepath.Join(dir, filename) + require.NoError(t, serialize.Write(path, data, 0o644)) + return path +} diff --git a/cannon/mipsevm/versions/state_test.go b/cannon/mipsevm/versions/state_test.go index c9bcc7e6f165..cf8eed639af9 100644 --- a/cannon/mipsevm/versions/state_test.go +++ b/cannon/mipsevm/versions/state_test.go @@ -1,14 +1,18 @@ +//go:build !cannon64 +// +build !cannon64 + package versions import ( "path/filepath" "testing" + "github.com/stretchr/testify/require" + "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/op-service/serialize" - "github.com/stretchr/testify/require" ) func TestNewFromState(t *testing.T) { @@ -49,10 +53,6 @@ func TestLoadStateFromFile(t *testing.T) { }) } -func TestLoadStateFromFile64(t *testing.T) { - t.Skip("TODO(#12205): Test asserting that cannon64 fails to decode a 32-bit state") -} - func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { tests := []struct { version StateVersion From 08cf004ffcc16135c0fff7838d4cadfbae8e4958 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 12:53:44 +0700 Subject: [PATCH 097/451] dependabot(gomod): bump github.com/multiformats/go-multiaddr (#12762) Bumps [github.com/multiformats/go-multiaddr](https://github.com/multiformats/go-multiaddr) from 0.13.0 to 0.14.0. - [Release notes](https://github.com/multiformats/go-multiaddr/releases) - [Commits](https://github.com/multiformats/go-multiaddr/compare/v0.13.0...v0.14.0) --- updated-dependencies: - dependency-name: github.com/multiformats/go-multiaddr dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3ff8ebb344ed..ec589c842052 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/minio/minio-go/v7 v7.0.80 github.com/multiformats/go-base32 v0.1.0 - github.com/multiformats/go-multiaddr v0.13.0 + github.com/multiformats/go-multiaddr v0.14.0 github.com/multiformats/go-multiaddr-dns v0.4.0 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 diff --git a/go.sum b/go.sum index 616fb9b7d453..3e2239b81bba 100644 --- a/go.sum +++ b/go.sum @@ -549,8 +549,8 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= -github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= +github.com/multiformats/go-multiaddr v0.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU= +github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4= github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU= github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= From 33a03313fcdec0de79f60949d830c37c36b79c7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:23:24 +0700 Subject: [PATCH 098/451] dependabot(gomod): bump github.com/fsnotify/fsnotify from 1.7.0 to 1.8.0 (#12761) Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/fsnotify/fsnotify/releases) - [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md) - [Commits](https://github.com/fsnotify/fsnotify/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/fsnotify/fsnotify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ec589c842052..3f61278e07c2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac github.com/ethereum/go-ethereum v1.14.11 - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/go-cmp v0.6.0 github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 diff --git a/go.sum b/go.sum index 3e2239b81bba..9794a2c370f4 100644 --- a/go.sum +++ b/go.sum @@ -213,8 +213,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= From dead005d9029327ad92c9fc871b0bf2a0398e3ef Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 1 Nov 2024 15:42:38 +0700 Subject: [PATCH 099/451] op-supervisor: improve logging, add update signals to trigger worker routines (#12770) --- op-supervisor/supervisor/backend/backend.go | 51 ++++++++++++++++--- .../supervisor/backend/cross/worker.go | 5 +- .../supervisor/backend/cross/worker_test.go | 2 +- op-supervisor/supervisor/backend/db/update.go | 13 +++-- .../backend/processors/chain_processor.go | 13 +++-- 5 files changed, 62 insertions(+), 22 deletions(-) diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index cd824bfa6d31..37596c5d8b6b 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -117,13 +117,6 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf } } - // for each chain initialize a chain processor service - for _, chainID := range chains { - logProcessor := processors.NewLogProcessor(chainID, su.chainDBs) - chainProcessor := processors.NewChainProcessor(su.logger, chainID, logProcessor, su.chainDBs) - su.chainProcessors[chainID] = chainProcessor - } - // initialize all cross-unsafe processors for _, chainID := range chains { worker := cross.NewCrossUnsafeWorker(su.logger, chainID, su.chainDBs) @@ -134,6 +127,13 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf worker := cross.NewCrossSafeWorker(su.logger, chainID, su.chainDBs) su.crossSafeProcessors[chainID] = worker } + // For each chain initialize a chain processor service, + // after cross-unsafe workers are ready to receive updates + for _, chainID := range chains { + logProcessor := processors.NewLogProcessor(chainID, su.chainDBs) + chainProcessor := processors.NewChainProcessor(su.logger, chainID, logProcessor, su.chainDBs, su.onIndexedLocalUnsafeData) + su.chainProcessors[chainID] = chainProcessor + } // the config has some RPC connections to attach to the chain-processors for _, rpc := range cfg.L2RPCs { @@ -145,6 +145,36 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf return nil } +// onIndexedLocalUnsafeData is called by the event indexing workers. +// This signals to cross-unsafe workers that there's data to index. +func (su *SupervisorBackend) onIndexedLocalUnsafeData() { + su.mu.RLock() + defer su.mu.RUnlock() + + // We signal all workers, since dependencies on a chain may be unblocked + // by new data on other chains. + // Busy workers don't block processing. + // The signal is picked up only if the worker is running in the background. + for _, w := range su.crossUnsafeProcessors { + w.OnNewData() + } +} + +// onNewLocalSafeData is called by the safety-indexing. +// This signals to cross-safe workers that there's data to index. +func (su *SupervisorBackend) onNewLocalSafeData() { + su.mu.RLock() + defer su.mu.RUnlock() + + // We signal all workers, since dependencies on a chain may be unblocked + // by new data on other chains. + // Busy workers don't block processing. + // The signal is picked up only if the worker is running in the background. + for _, w := range su.crossSafeProcessors { + w.OnNewData() + } +} + // openChainDBs initializes all the DB resources of a specific chain. // It is a sub-task of initResources. func (su *SupervisorBackend) openChainDBs(chainID types.ChainID) error { @@ -443,7 +473,12 @@ func (su *SupervisorBackend) UpdateLocalSafe(ctx context.Context, chainID types. su.mu.RLock() defer su.mu.RUnlock() - return su.chainDBs.UpdateLocalSafe(chainID, derivedFrom, lastDerived) + err := su.chainDBs.UpdateLocalSafe(chainID, derivedFrom, lastDerived) + if err != nil { + return err + } + su.onNewLocalSafeData() + return nil } func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error { diff --git a/op-supervisor/supervisor/backend/cross/worker.go b/op-supervisor/supervisor/backend/cross/worker.go index b80c78442c6b..a555a5f11b13 100644 --- a/op-supervisor/supervisor/backend/cross/worker.go +++ b/op-supervisor/supervisor/backend/cross/worker.go @@ -71,7 +71,7 @@ func (s *Worker) worker() { return } if errors.Is(err, types.ErrFuture) { - s.log.Debug("Failed to process work", "err", err) + s.log.Debug("Worker awaits more data", "err", err) } else { s.log.Warn("Failed to process work", "err", err) } @@ -92,14 +92,13 @@ func (s *Worker) worker() { } } -func (s *Worker) OnNewData() error { +func (s *Worker) OnNewData() { // signal that we have something to process select { case s.poke <- struct{}{}: default: // already requested an update } - return nil } func (s *Worker) Close() { diff --git a/op-supervisor/supervisor/backend/cross/worker_test.go b/op-supervisor/supervisor/backend/cross/worker_test.go index 64f4d2d8d12f..03fda620350b 100644 --- a/op-supervisor/supervisor/backend/cross/worker_test.go +++ b/op-supervisor/supervisor/backend/cross/worker_test.go @@ -55,7 +55,7 @@ func TestWorker(t *testing.T) { return count == 1 }, 2*time.Second, 100*time.Millisecond) // when OnNewData is called, the worker runs again - require.NoError(t, w.OnNewData()) + w.OnNewData() require.Eventually(t, func() bool { return count == 2 }, 2*time.Second, 100*time.Millisecond) diff --git a/op-supervisor/supervisor/backend/db/update.go b/op-supervisor/supervisor/backend/db/update.go index 932b19875575..7ae7fde58a8b 100644 --- a/op-supervisor/supervisor/backend/db/update.go +++ b/op-supervisor/supervisor/backend/db/update.go @@ -27,11 +27,11 @@ func (db *ChainsDB) SealBlock(chain types.ChainID, block eth.BlockRef) error { if !ok { return fmt.Errorf("cannot SealBlock: %w: %v", types.ErrUnknownChain, chain) } - db.logger.Debug("Updating local unsafe", "chain", chain, "block", block) err := logDB.SealBlock(block.ParentHash, block.ID(), block.Time) if err != nil { return fmt.Errorf("failed to seal block %v: %w", block, err) } + db.logger.Info("Updated local unsafe", "chain", chain, "block", block) return nil } @@ -57,8 +57,8 @@ func (db *ChainsDB) UpdateCrossUnsafe(chain types.ChainID, crossUnsafe types.Blo if !ok { return fmt.Errorf("cannot UpdateCrossUnsafe: %w: %s", types.ErrUnknownChain, chain) } - db.logger.Debug("Updating cross unsafe", "chain", chain, "crossUnsafe", crossUnsafe) v.Set(crossUnsafe) + db.logger.Info("Updated cross-unsafe", "chain", chain, "crossUnsafe", crossUnsafe) return nil } @@ -67,8 +67,11 @@ func (db *ChainsDB) UpdateCrossSafe(chain types.ChainID, l1View eth.BlockRef, la if !ok { return fmt.Errorf("cannot UpdateCrossSafe: %w: %s", types.ErrUnknownChain, chain) } - db.logger.Debug("Updating cross safe", "chain", chain, "l1View", l1View, "lastCrossDerived", lastCrossDerived) - return crossDB.AddDerived(l1View, lastCrossDerived) + if err := crossDB.AddDerived(l1View, lastCrossDerived); err != nil { + return err + } + db.logger.Info("Updated cross-safe", "chain", chain, "l1View", l1View, "lastCrossDerived", lastCrossDerived) + return nil } func (db *ChainsDB) UpdateFinalizedL1(finalized eth.BlockRef) error { @@ -79,7 +82,7 @@ func (db *ChainsDB) UpdateFinalizedL1(finalized eth.BlockRef) error { if v := db.finalizedL1.Value; v.Number > finalized.Number { return fmt.Errorf("cannot rewind finalized L1 head from %s to %s", v, finalized) } - db.logger.Debug("Updating finalized L1", "finalizedL1", finalized) db.finalizedL1.Value = finalized + db.logger.Info("Updated finalized L1", "finalizedL1", finalized) return nil } diff --git a/op-supervisor/supervisor/backend/processors/chain_processor.go b/op-supervisor/supervisor/backend/processors/chain_processor.go index 7f4687086030..688f68ac6119 100644 --- a/op-supervisor/supervisor/backend/processors/chain_processor.go +++ b/op-supervisor/supervisor/backend/processors/chain_processor.go @@ -55,8 +55,8 @@ type ChainProcessor struct { // channel with capacity of 1, full if there is work to do newHead chan struct{} - // channel with capacity of 1, to signal work complete if running in synchroneous mode - out chan struct{} + // to signal to the other services that new indexed data is available + onIndexed func() // lifetime management of the chain processor ctx context.Context @@ -64,7 +64,7 @@ type ChainProcessor struct { wg sync.WaitGroup } -func NewChainProcessor(log log.Logger, chain types.ChainID, processor LogProcessor, rewinder DatabaseRewinder) *ChainProcessor { +func NewChainProcessor(log log.Logger, chain types.ChainID, processor LogProcessor, rewinder DatabaseRewinder, onIndexed func()) *ChainProcessor { ctx, cancel := context.WithCancel(context.Background()) out := &ChainProcessor{ log: log.New("chain", chain), @@ -73,7 +73,7 @@ func NewChainProcessor(log log.Logger, chain types.ChainID, processor LogProcess processor: processor, rewinder: rewinder, newHead: make(chan struct{}, 1), - out: make(chan struct{}, 1), + onIndexed: onIndexed, ctx: ctx, cancel: cancel, } @@ -134,7 +134,7 @@ func (s *ChainProcessor) work() { target := s.nextNum() if err := s.update(target); err != nil { if errors.Is(err, ethereum.NotFound) { - s.log.Info("Cannot find next block yet", "target", target, "err", err) + s.log.Debug("Event-indexer cannot find next block yet", "target", target, "err", err) } else if errors.Is(err, types.ErrNoRPCSource) { s.log.Warn("No RPC source configured, cannot process new blocks") } else { @@ -192,7 +192,10 @@ func (s *ChainProcessor) update(nextNum uint64) error { // If no logs were written successfully then the rewind wouldn't have done anything anyway. s.log.Error("Failed to rewind after error processing block", "block", next, "err", err) } + return err } + s.log.Info("Indexed block events", "block", next, "txs", len(receipts)) + s.onIndexed() return nil } From 3d03f5f731c6c3a2dae6b74397ae392910de1f8b Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 1 Nov 2024 05:35:58 -0400 Subject: [PATCH 100/451] feat: log diff in interfaces check (#12757) --- packages/contracts-bedrock/scripts/checks/interfaces/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/contracts-bedrock/scripts/checks/interfaces/main.go b/packages/contracts-bedrock/scripts/checks/interfaces/main.go index 5d62a2e3b4ff..8bdbf79514e2 100644 --- a/packages/contracts-bedrock/scripts/checks/interfaces/main.go +++ b/packages/contracts-bedrock/scripts/checks/interfaces/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "os" "path/filepath" "runtime" @@ -301,6 +302,7 @@ func compareABIs(abi1, abi2 json.RawMessage) (bool, error) { // Compare using go-cmp diff := cmp.Diff(data1, data2) if diff != "" { + log.Printf("ABI diff: %s", diff) return false, nil } return true, nil From 15c50ee9a7621eae532d8c797964cf884bfea54b Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Fri, 1 Nov 2024 17:10:25 +0700 Subject: [PATCH 101/451] refactor: Move interop e2e test contracts to e2eutils/interop. (#12772) * refactor: Move interop e2e test contracts to e2eutils/interop. * refactor: Move interop e2e test contracts to e2eutils/interop/contracts. --- .../interop/contracts/bindings/emit/emit.go | 0 .../interop/contracts/bindings/inbox/inbox.go | 0 .../contracts/bindings/systemconfig/systemconfig.go | 0 .../build/ICrossL2Inbox.sol/ICrossL2Inbox.abi | 0 .../build/ICrossL2Inbox.sol/ICrossL2Inbox.bin | 0 .../build/ICrossL2Inbox.sol/ICrossL2Inbox.json | 0 .../build/ISystemConfig.sol/ISystemConfig.abi | 0 .../build/ISystemConfig.sol/ISystemConfig.bin | 0 .../build/ISystemConfig.sol/ISystemConfig.json | 0 .../interop/contracts/build/emit.sol/EmitEvent.abi | 0 .../interop/contracts/build/emit.sol/EmitEvent.bin | 0 .../interop/contracts/build/emit.sol/EmitEvent.json | 0 op-e2e/{ => e2eutils}/interop/contracts/foundry.toml | 0 op-e2e/{ => e2eutils}/interop/contracts/generate.sh | 0 .../interop/contracts/src/ICrossL2Inbox.sol | 0 .../interop/contracts/src/ISystemConfig.sol | 0 op-e2e/{ => e2eutils}/interop/contracts/src/emit.sol | 0 .../contracts/test-artifacts/emit.sol/EmitEvent.abi | 0 .../contracts/test-artifacts/emit.sol/EmitEvent.bin | 0 .../contracts/test-artifacts/emit.sol/EmitEvent.json | 0 op-e2e/interop/supersystem.go | 11 ++++++----- 21 files changed, 6 insertions(+), 5 deletions(-) rename op-e2e/{ => e2eutils}/interop/contracts/bindings/emit/emit.go (100%) rename op-e2e/{ => e2eutils}/interop/contracts/bindings/inbox/inbox.go (100%) rename op-e2e/{ => e2eutils}/interop/contracts/bindings/systemconfig/systemconfig.go (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/emit.sol/EmitEvent.abi (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/emit.sol/EmitEvent.bin (100%) rename op-e2e/{ => e2eutils}/interop/contracts/build/emit.sol/EmitEvent.json (100%) rename op-e2e/{ => e2eutils}/interop/contracts/foundry.toml (100%) rename op-e2e/{ => e2eutils}/interop/contracts/generate.sh (100%) rename op-e2e/{ => e2eutils}/interop/contracts/src/ICrossL2Inbox.sol (100%) rename op-e2e/{ => e2eutils}/interop/contracts/src/ISystemConfig.sol (100%) rename op-e2e/{ => e2eutils}/interop/contracts/src/emit.sol (100%) rename op-e2e/{ => e2eutils}/interop/contracts/test-artifacts/emit.sol/EmitEvent.abi (100%) rename op-e2e/{ => e2eutils}/interop/contracts/test-artifacts/emit.sol/EmitEvent.bin (100%) rename op-e2e/{ => e2eutils}/interop/contracts/test-artifacts/emit.sol/EmitEvent.json (100%) diff --git a/op-e2e/interop/contracts/bindings/emit/emit.go b/op-e2e/e2eutils/interop/contracts/bindings/emit/emit.go similarity index 100% rename from op-e2e/interop/contracts/bindings/emit/emit.go rename to op-e2e/e2eutils/interop/contracts/bindings/emit/emit.go diff --git a/op-e2e/interop/contracts/bindings/inbox/inbox.go b/op-e2e/e2eutils/interop/contracts/bindings/inbox/inbox.go similarity index 100% rename from op-e2e/interop/contracts/bindings/inbox/inbox.go rename to op-e2e/e2eutils/interop/contracts/bindings/inbox/inbox.go diff --git a/op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go b/op-e2e/e2eutils/interop/contracts/bindings/systemconfig/systemconfig.go similarity index 100% rename from op-e2e/interop/contracts/bindings/systemconfig/systemconfig.go rename to op-e2e/e2eutils/interop/contracts/bindings/systemconfig/systemconfig.go diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi b/op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi similarity index 100% rename from op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi rename to op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.abi diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin b/op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin similarity index 100% rename from op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin rename to op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.bin diff --git a/op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json b/op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json similarity index 100% rename from op-e2e/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json rename to op-e2e/e2eutils/interop/contracts/build/ICrossL2Inbox.sol/ICrossL2Inbox.json diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi b/op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi similarity index 100% rename from op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi rename to op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.abi diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin b/op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin similarity index 100% rename from op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin rename to op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.bin diff --git a/op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json b/op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json similarity index 100% rename from op-e2e/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json rename to op-e2e/e2eutils/interop/contracts/build/ISystemConfig.sol/ISystemConfig.json diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi b/op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.abi similarity index 100% rename from op-e2e/interop/contracts/build/emit.sol/EmitEvent.abi rename to op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.abi diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin b/op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.bin similarity index 100% rename from op-e2e/interop/contracts/build/emit.sol/EmitEvent.bin rename to op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.bin diff --git a/op-e2e/interop/contracts/build/emit.sol/EmitEvent.json b/op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.json similarity index 100% rename from op-e2e/interop/contracts/build/emit.sol/EmitEvent.json rename to op-e2e/e2eutils/interop/contracts/build/emit.sol/EmitEvent.json diff --git a/op-e2e/interop/contracts/foundry.toml b/op-e2e/e2eutils/interop/contracts/foundry.toml similarity index 100% rename from op-e2e/interop/contracts/foundry.toml rename to op-e2e/e2eutils/interop/contracts/foundry.toml diff --git a/op-e2e/interop/contracts/generate.sh b/op-e2e/e2eutils/interop/contracts/generate.sh similarity index 100% rename from op-e2e/interop/contracts/generate.sh rename to op-e2e/e2eutils/interop/contracts/generate.sh diff --git a/op-e2e/interop/contracts/src/ICrossL2Inbox.sol b/op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol similarity index 100% rename from op-e2e/interop/contracts/src/ICrossL2Inbox.sol rename to op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol diff --git a/op-e2e/interop/contracts/src/ISystemConfig.sol b/op-e2e/e2eutils/interop/contracts/src/ISystemConfig.sol similarity index 100% rename from op-e2e/interop/contracts/src/ISystemConfig.sol rename to op-e2e/e2eutils/interop/contracts/src/ISystemConfig.sol diff --git a/op-e2e/interop/contracts/src/emit.sol b/op-e2e/e2eutils/interop/contracts/src/emit.sol similarity index 100% rename from op-e2e/interop/contracts/src/emit.sol rename to op-e2e/e2eutils/interop/contracts/src/emit.sol diff --git a/op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.abi b/op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.abi similarity index 100% rename from op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.abi rename to op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.abi diff --git a/op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.bin b/op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.bin similarity index 100% rename from op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.bin rename to op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.bin diff --git a/op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.json b/op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.json similarity index 100% rename from op-e2e/interop/contracts/test-artifacts/emit.sol/EmitEvent.json rename to op-e2e/e2eutils/interop/contracts/test-artifacts/emit.sol/EmitEvent.json diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index 9f78c543bdcf..f18c7475073e 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -11,9 +11,6 @@ import ( "testing" "time" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" - "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/systemconfig" - "github.com/ethereum-optimism/optimism/op-service/predeploys" "github.com/ethereum/go-ethereum/eth/ethconfig" gn "github.com/ethereum/go-ethereum/node" @@ -28,6 +25,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/interop/contracts/bindings/emit" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/interop/contracts/bindings/inbox" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/interop/contracts/bindings/systemconfig" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + bss "github.com/ethereum-optimism/optimism/op-batcher/batcher" batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" @@ -39,8 +42,6 @@ import ( "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/opnode" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/services" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils" - "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/emit" - "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/inbox" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" "github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/p2p" From 28da3f803d2650651ed3286e957b5e2e6eaa1e56 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 1 Nov 2024 18:22:54 +0700 Subject: [PATCH 102/451] interop-devnet: add grafana/loki/promtrail (#12773) * interop-devnet: add grafana/loki/promtrail Revival of PR https://github.com/ethereum-optimism/optimism/pull/11628 to enhance local interop devnet. Co-authored-by: Ethen Pociask Co-authored-by: Samuel Laferriere * interop-devnet: use custom grafana port * interop-devnet: fix ports * interop-devnet: enable op-supervisor metrics --------- Co-authored-by: Ethen Pociask Co-authored-by: Samuel Laferriere --- interop-devnet/docker-compose.yml | 43 + interop-devnet/justfile | 9 +- interop-devnet/monitoring/grafana.env | 1 + .../op_stack_compose_dashboard.json | 2203 +++++++++++++++++ .../grafana/provisioning/dashboards/all.yml | 11 + .../grafana/provisioning/datasources/all.yml | 24 + interop-devnet/monitoring/loki/config.yaml | 29 + .../monitoring/prometheus/prometheus.yml | 43 + .../monitoring/promtail/config.yaml | 32 + 9 files changed, 2394 insertions(+), 1 deletion(-) create mode 100644 interop-devnet/monitoring/grafana.env create mode 100644 interop-devnet/monitoring/grafana/dashboards/op_stack_compose_dashboard.json create mode 100644 interop-devnet/monitoring/grafana/provisioning/dashboards/all.yml create mode 100644 interop-devnet/monitoring/grafana/provisioning/datasources/all.yml create mode 100644 interop-devnet/monitoring/loki/config.yaml create mode 100644 interop-devnet/monitoring/prometheus/prometheus.yml create mode 100644 interop-devnet/monitoring/promtail/config.yaml diff --git a/interop-devnet/docker-compose.yml b/interop-devnet/docker-compose.yml index 97ddcbda655f..de97c9967b64 100644 --- a/interop-devnet/docker-compose.yml +++ b/interop-devnet/docker-compose.yml @@ -94,6 +94,8 @@ services: --rpc.port=8545 --rpc.enable-admin --l2-rpcs="ws://l2-a:8546,ws://l2-b:8546" + environment: + OP_SUPERVISOR_METRICS_ENABLED: "true" l2-a: depends_on: @@ -352,3 +354,44 @@ services: # OP_BATCHER_DATA_AVAILABILITY_TYPE: blobs env_file: - "${PWD}/../.devnet-interop/env/l2/900201/op-batcher.env" + + grafana: + image: grafana/grafana:11.1.0 + restart: unless-stopped + env_file: + - monitoring/grafana.env + volumes: + - ./monitoring/grafana/provisioning/:/etc/grafana/provisioning/:ro + - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards + # - grafana_data:/var/lib/grafana + ports: + - 3300:3000 + + prometheus: + image: prom/prometheus:latest + restart: unless-stopped + volumes: + - ./monitoring/prometheus:/etc/prometheus + # - prometheus_data:/prometheus + ports: + - 3090:9090 + command: --config.file=/etc/prometheus/prometheus.yml --log.level=debug + + loki: + image: grafana/loki:3.1.1 + restart: unless-stopped + volumes: + - ./monitoring/loki:/etc/loki + ports: + - 3200:3200 + command: -config.file=/etc/loki/config.yaml + + promtail: + image: grafana/promtail:3.1.1 + restart: unless-stopped + volumes: + # uncomment to scrape system logs + # - /var/log:/var/log + - ./monitoring/promtail:/etc/promtail + - /var/run/docker.sock:/var/run/docker.sock # Mount Docker socket to read container logs + command: -config.file=/etc/promtail/config.yaml diff --git a/interop-devnet/justfile b/interop-devnet/justfile index 7aa215dbbd7f..6af11ccc8fb8 100644 --- a/interop-devnet/justfile +++ b/interop-devnet/justfile @@ -20,14 +20,21 @@ devnet-down: # stops services, does not remove containers/networks docker compose stop +devnet-metrics-up: + docker compose up -d prometheus grafana loki promtail + +devnet-metrics-down: + docker compose down -d prometheus grafana loki promtail + devnet-clean: - rm -rf ../.devnet-interop # Stops services, and removes containers/networks docker compose down # Now manually clean up the related images and volumes # Note: `justfile` interprets the curly brackets. So we escape them, by wrapping it with more, as a string, like Jinja2. docker image ls 'interop-devnet*' --format='{{ '{{.Repository}}' }}' | xargs -r docker rmi docker volume ls --filter name=interop-devnet --format='{{ '{{.Name}}' }}' | xargs -r docker volume rm + # docker compose down needs the env files before being able to shut down, so remove the devnet config resources last + rm -rf ../.devnet-interop devnet-logs: docker compose logs -f diff --git a/interop-devnet/monitoring/grafana.env b/interop-devnet/monitoring/grafana.env new file mode 100644 index 000000000000..3a7446dfccfa --- /dev/null +++ b/interop-devnet/monitoring/grafana.env @@ -0,0 +1 @@ +GF_SECURITY_ADMIN_PASSWORD=admin diff --git a/interop-devnet/monitoring/grafana/dashboards/op_stack_compose_dashboard.json b/interop-devnet/monitoring/grafana/dashboards/op_stack_compose_dashboard.json new file mode 100644 index 000000000000..c3d34c9d8a56 --- /dev/null +++ b/interop-devnet/monitoring/grafana/dashboards/op_stack_compose_dashboard.json @@ -0,0 +1,2203 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 31, + "panels": [], + "title": "Logs", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "loki-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 24, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "loki-datasource" + }, + "expr": "{container=\"ops-bedrock-op-batcher-1\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "Batcher Logs", + "type": "logs" + }, + { + "datasource": { + "type": "loki", + "uid": "loki-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 32, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "loki-datasource" + }, + "editorMode": "code", + "expr": "{container=\"ops-bedrock-op-proposer-1\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "Proposer Logs", + "type": "logs" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 14, + "panels": [], + "title": "Batcher", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 20, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_pending_blocks_count", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "pending_blocks_count {{stage}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Number of pending blocks, not added to a channel yet.", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 19, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_input_bytes", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "input {{stage}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_output_bytes", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "output", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Total number of input/output bytes per channel.", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 30 + }, + "id": 17, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_pending_blocks_bytes_total", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "op_batcher_default_pending_blocks_bytes_current", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Total size of transactions in pending blocks as they are fetched from L2", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 30 + }, + "id": 16, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_blocks_added_count", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total number of blocks added to current channel.", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 38 + }, + "id": 18, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_channel_num_frames", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total number of frames of closed channel.", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 25, + "interval": "2s", + "options": { + "displayMode": "basic", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_blob_used_bytes_bucket", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Blob sizes", + "type": "bargauge" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 50 + }, + "id": 26, + "panels": [], + "title": "Batcher txmgr", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 51 + }, + "id": 21, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_last_confirm_unix", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_last_publish_unix", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Batcher Tx Publish/Confirm Times", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "TODO: batcher_tx_total is 2x txs sent b/c of bug. See https://github.com/ethereum-optimism/optimism/pull/11438", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 51 + }, + "id": 15, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_batcher_tx_total", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_confirm_total", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_current_nonce", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Batcher Txs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 59 + }, + "id": 23, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_pending_txs", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Batcher Pending Txs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 59 + }, + "id": 22, + "interval": "2s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_txmgr_tx_confirmed_latency_ms", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tx confirmation latency", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 6, + "panels": [], + "title": "Sequencer", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 68 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l2_unsafe\", layer=\"l2\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L2 Unsafe Block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 5, + "y": 68 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l2_safe\", layer=\"l2\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L2 Safe Block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 10, + "y": 68 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l2_finalized\", layer=\"l2\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L2 Finalized Block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "A" + }, + "properties": [ + { + "id": "displayName", + "value": "Balance" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 15, + "y": 68 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hoverProximity": -46, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(op_node_default_l1_reorg_depth_count[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L1 Reorg Count", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 72 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l1_head\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L1 Head", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 5, + "y": 72 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l1_safe\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L1 Safe Block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 10, + "y": 72 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_node_default_refs_number{type=\"l1_finalized\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "L1 Finalized Block", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 76 + }, + "id": 5, + "panels": [], + "title": "Balances", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 0.5 + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 77 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_balance", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Batcher Balance", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 80 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 54 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_proposer_default_balance", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Proposer Balance", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "A" + }, + "properties": [ + { + "id": "displayName", + "value": "Balance" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 83 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hoverProximity": -46, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_batcher_default_balance", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Batcher Balance", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "A" + }, + "properties": [ + { + "id": "displayName", + "value": "Balance" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 83 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddshms3dlineoe" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "op_proposer_default_balance", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Proposer Balance", + "type": "timeseries" + } + ], + "refresh": "10s", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "OP Stack Compose", + "uid": "ddshn0bgt86ioc", + "version": 1, + "weekStart": "" +} diff --git a/interop-devnet/monitoring/grafana/provisioning/dashboards/all.yml b/interop-devnet/monitoring/grafana/provisioning/dashboards/all.yml new file mode 100644 index 000000000000..e281a1e592c8 --- /dev/null +++ b/interop-devnet/monitoring/grafana/provisioning/dashboards/all.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: true + options: + path: /var/lib/grafana/dashboards diff --git a/interop-devnet/monitoring/grafana/provisioning/datasources/all.yml b/interop-devnet/monitoring/grafana/provisioning/datasources/all.yml new file mode 100644 index 000000000000..8a97e63640d4 --- /dev/null +++ b/interop-devnet/monitoring/grafana/provisioning/datasources/all.yml @@ -0,0 +1,24 @@ +apiVersion: 1 + +deleteDatasources: + - name: "Prometheus" + +datasources: + - access: "proxy" + editable: true + is_default: true + name: "Prometheus" + uid: "ddshms3dlineoe" + org_id: 1 + type: "prometheus" + url: "http://prometheus:9090" + version: 1 + + - access: "proxy" + editable: true + name: "Loki" + uid: "loki-datasource" + org_id: 1 + type: "loki" + url: "http://loki:3200" + version: 1 diff --git a/interop-devnet/monitoring/loki/config.yaml b/interop-devnet/monitoring/loki/config.yaml new file mode 100644 index 000000000000..f5350cfc2444 --- /dev/null +++ b/interop-devnet/monitoring/loki/config.yaml @@ -0,0 +1,29 @@ +auth_enabled: false + +server: + http_listen_port: 3200 + +common: + instance_addr: 127.0.0.1 + path_prefix: /loki + storage: + filesystem: + chunks_directory: /loki/chunks + rules_directory: /loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +analytics: + reporting_enabled: false diff --git a/interop-devnet/monitoring/prometheus/prometheus.yml b/interop-devnet/monitoring/prometheus/prometheus.yml new file mode 100644 index 000000000000..c2a7e6fe3a62 --- /dev/null +++ b/interop-devnet/monitoring/prometheus/prometheus.yml @@ -0,0 +1,43 @@ +global: + scrape_interval: 4s + evaluation_interval: 15s + +scrape_configs: + - job_name: "l1-geth" + static_configs: + - targets: ["l1:6060"] + metrics_path: /debug/metrics/prometheus + + - job_name: "l2-a-geth" + static_configs: + - targets: ["l2-a:6060"] + metrics_path: /debug/metrics/prometheus + - job_name: "l2-b-geth" + static_configs: + - targets: ["l2-b:6060"] + metrics_path: /debug/metrics/prometheus + + - job_name: "op-node-a" + static_configs: + - targets: ["op-node-a:7300"] + - job_name: "op-node-b" + static_configs: + - targets: ["op-node-b:7300"] + + - job_name: "op-batcher-a" + static_configs: + - targets: ["op-batcher-a:7300"] + - job_name: "op-batcher-b" + static_configs: + - targets: ["op-batcher-b:7300"] + + - job_name: "op-proposer-a" + static_configs: + - targets: ["op-proposer-a:7300"] + - job_name: "op-proposer-b" + static_configs: + - targets: ["op-proposer-b:7300"] + + - job_name: "op-supervisor" + static_configs: + - targets: ["op-supervisor:7300"] diff --git a/interop-devnet/monitoring/promtail/config.yaml b/interop-devnet/monitoring/promtail/config.yaml new file mode 100644 index 000000000000..1f724873c2d0 --- /dev/null +++ b/interop-devnet/monitoring/promtail/config.yaml @@ -0,0 +1,32 @@ +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://loki:3200/loki/api/v1/push + +scrape_configs: + # Uncomment to scrape system logs + # - job_name: system + # static_configs: + # - targets: + # - localhost + # labels: + # job: varlogs + # __path__: /var/log/*log + # This scrapes docker container logs + # copied from https://stackoverflow.com/questions/74776432/with-promtail-how-do-i-only-keep-log-messages-for-specified-docker-containers + - job_name: docker + docker_sd_configs: + - host: unix:///var/run/docker.sock + filters: + - name: name + # Filter logging to just our containers + values: ["op-batcher-*", "op-proposer-*", "op-node-*", "op-supervisor-*"] + relabel_configs: + - source_labels: ["__meta_docker_container_name"] + regex: "/(.*)" + target_label: "container" From cc22e24361e822182bb73f6cc0fcd69597c73557 Mon Sep 17 00:00:00 2001 From: Inphi Date: Fri, 1 Nov 2024 07:32:57 -0700 Subject: [PATCH 103/451] cannon: Avoid running expensive program tests in merge queue (#12779) --- cannon/mipsevm/tests/evm_common_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index 2aa1def61ca7..f61ee0850d51 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -875,6 +875,10 @@ func TestEVMFault(t *testing.T) { } func TestHelloEVM(t *testing.T) { + if os.Getenv("SKIP_SLOW_TESTS") == "true" { + t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") + } + t.Parallel() versions := GetMipsVersionTestCases(t) @@ -918,6 +922,10 @@ func TestHelloEVM(t *testing.T) { } func TestClaimEVM(t *testing.T) { + if os.Getenv("SKIP_SLOW_TESTS") == "true" { + t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") + } + t.Parallel() versions := GetMipsVersionTestCases(t) @@ -959,6 +967,10 @@ func TestClaimEVM(t *testing.T) { } func TestEntryEVM(t *testing.T) { + if os.Getenv("SKIP_SLOW_TESTS") == "true" { + t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") + } + t.Parallel() versions := GetMipsVersionTestCases(t) From 1f8044e52101deadde766128f90ff23b4c7317f4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 1 Nov 2024 22:07:57 +0700 Subject: [PATCH 104/451] op-supervisor: fix executing message decoding (#12782) --- .../backend/processors/contracts/l2inbox.go | 118 ------------------ .../processors/contracts/l2inbox_test.go | 75 ----------- .../backend/processors/executing_message.go | 38 ++++++ .../processors/executing_message_test.go | 58 +++++++++ .../backend/processors/log_processor.go | 30 ++--- .../backend/processors/log_processor_test.go | 14 +-- 6 files changed, 110 insertions(+), 223 deletions(-) delete mode 100644 op-supervisor/supervisor/backend/processors/contracts/l2inbox.go delete mode 100644 op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go create mode 100644 op-supervisor/supervisor/backend/processors/executing_message.go create mode 100644 op-supervisor/supervisor/backend/processors/executing_message_test.go diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go deleted file mode 100644 index 3d0eb1a6b3b0..000000000000 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox.go +++ /dev/null @@ -1,118 +0,0 @@ -package contracts - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/big" - - "github.com/ethereum-optimism/optimism/op-service/predeploys" - "github.com/ethereum-optimism/optimism/op-service/solabi" - "github.com/ethereum-optimism/optimism/op-service/sources/batching" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" - "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots" - "github.com/ethereum/go-ethereum/common" - ethTypes "github.com/ethereum/go-ethereum/core/types" -) - -const ( - eventExecutingMessage = "ExecutingMessage" -) - -var ( - ErrEventNotFound = errors.New("event not found") -) - -type contractIdentifier struct { - // Origin represents the address that initiated the message - // it is used in combination with the MsgHash to uniquely identify a message - // and is hashed into the log hash, not stored directly. - Origin common.Address - LogIndex *big.Int - BlockNumber *big.Int - ChainId *big.Int - Timestamp *big.Int -} - -type CrossL2Inbox struct { - contract *batching.BoundContract -} - -func NewCrossL2Inbox() *CrossL2Inbox { - abi := snapshots.LoadCrossL2InboxABI() - return &CrossL2Inbox{ - contract: batching.NewBoundContract(abi, predeploys.CrossL2InboxAddr), - } -} - -func (i *CrossL2Inbox) DecodeExecutingMessageLog(l *ethTypes.Log) (types.ExecutingMessage, error) { - if l.Address != i.contract.Addr() { - return types.ExecutingMessage{}, fmt.Errorf("%w: log not from CrossL2Inbox", ErrEventNotFound) - } - // use DecodeEvent to check the name of the event - // but the actual decoding is done manually to extract the contract identifier - name, _, err := i.contract.DecodeEvent(l) - if errors.Is(err, batching.ErrUnknownEvent) { - return types.ExecutingMessage{}, fmt.Errorf("%w: %v", ErrEventNotFound, err.Error()) - } else if err != nil { - return types.ExecutingMessage{}, fmt.Errorf("failed to decode event: %w", err) - } - if name != eventExecutingMessage { - return types.ExecutingMessage{}, fmt.Errorf("%w: event %v not an ExecutingMessage event", ErrEventNotFound, name) - } - // the second topic is the hash of the payload (the first is the event ID) - msgHash := l.Topics[1] - // the first 32 bytes of the data are the msgHash, so we skip them - identifierBytes := bytes.NewReader(l.Data[32:]) - identifier, err := identifierFromBytes(identifierBytes) - if err != nil { - return types.ExecutingMessage{}, fmt.Errorf("failed to read contract identifier: %w", err) - } - chainID, err := types.ChainIDFromBig(identifier.ChainId).ToUInt32() - if err != nil { - return types.ExecutingMessage{}, fmt.Errorf("failed to convert chain ID %v to uint32: %w", identifier.ChainId, err) - } - hash := types.PayloadHashToLogHash(msgHash, identifier.Origin) - return types.ExecutingMessage{ - Chain: types.ChainIndex(chainID), // TODO(#11105): translate chain ID to chain index - Hash: hash, - BlockNum: identifier.BlockNumber.Uint64(), - LogIdx: uint32(identifier.LogIndex.Uint64()), - Timestamp: identifier.Timestamp.Uint64(), - }, nil -} - -// identifierFromBytes reads a contract identifier from a byte stream. -// it follows the spec and matches the CrossL2Inbox.json definition, -// rather than relying on reflection, as that can be error-prone regarding struct ordering -func identifierFromBytes(identifierBytes io.Reader) (contractIdentifier, error) { - origin, err := solabi.ReadAddress(identifierBytes) - if err != nil { - return contractIdentifier{}, fmt.Errorf("failed to read origin address: %w", err) - } - originAddr := common.BytesToAddress(origin[:]) - blockNumber, err := solabi.ReadUint256(identifierBytes) - if err != nil { - return contractIdentifier{}, fmt.Errorf("failed to read block number: %w", err) - } - logIndex, err := solabi.ReadUint256(identifierBytes) - if err != nil { - return contractIdentifier{}, fmt.Errorf("failed to read log index: %w", err) - } - timestamp, err := solabi.ReadUint256(identifierBytes) - if err != nil { - return contractIdentifier{}, fmt.Errorf("failed to read timestamp: %w", err) - } - chainID, err := solabi.ReadUint256(identifierBytes) - if err != nil { - return contractIdentifier{}, fmt.Errorf("failed to read chain ID: %w", err) - } - return contractIdentifier{ - Origin: originAddr, - BlockNumber: blockNumber, - LogIndex: logIndex, - Timestamp: timestamp, - ChainId: chainID, - }, nil -} diff --git a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go b/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go deleted file mode 100644 index b3973c61e30b..000000000000 --- a/op-supervisor/supervisor/backend/processors/contracts/l2inbox_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package contracts - -import ( - "bytes" - "math/big" - "testing" - - "github.com/ethereum-optimism/optimism/op-service/predeploys" - "github.com/ethereum-optimism/optimism/op-service/sources/batching" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" - "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots" - "github.com/ethereum/go-ethereum/common" - ethTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/require" -) - -func TestDecodeExecutingMessageEvent(t *testing.T) { - inbox := NewCrossL2Inbox() - payload := bytes.Repeat([]byte{0xaa, 0xbb}, 50) - payloadHash := crypto.Keccak256Hash(payload) - expected := types.ExecutingMessage{ - Chain: 42424, // TODO(#11105): translate chain ID to chain index - BlockNum: 12345, - LogIdx: 98, - Timestamp: 9578295, - } - contractIdent := contractIdentifier{ - Origin: common.Address{0xbb, 0xcc}, - ChainId: new(big.Int).SetUint64(uint64(expected.Chain)), // TODO(#11105): translate chain ID to chain index - BlockNumber: new(big.Int).SetUint64(expected.BlockNum), - Timestamp: new(big.Int).SetUint64(expected.Timestamp), - LogIndex: new(big.Int).SetUint64(uint64(expected.LogIdx)), - } - expected.Hash = types.PayloadHashToLogHash(payloadHash, contractIdent.Origin) - abi := snapshots.LoadCrossL2InboxABI() - validData, err := abi.Events[eventExecutingMessage].Inputs.Pack(payloadHash, contractIdent) - require.NoError(t, err) - createValidLog := func() *ethTypes.Log { - //protoHack := bytes.Repeat([]byte{0x00}, 32*5) - return ðTypes.Log{ - Address: predeploys.CrossL2InboxAddr, - Topics: []common.Hash{abi.Events[eventExecutingMessage].ID, payloadHash}, - Data: validData, - } - } - - t.Run("ParseValid", func(t *testing.T) { - l := createValidLog() - result, err := inbox.DecodeExecutingMessageLog(l) - require.NoError(t, err) - require.Equal(t, expected, result) - }) - - t.Run("IgnoreIncorrectContract", func(t *testing.T) { - l := createValidLog() - l.Address = common.Address{0xff} - _, err := inbox.DecodeExecutingMessageLog(l) - require.ErrorIs(t, err, ErrEventNotFound) - }) - - t.Run("IgnoreWrongEvent", func(t *testing.T) { - l := createValidLog() - l.Topics[0] = common.Hash{0xbb} - _, err := inbox.DecodeExecutingMessageLog(l) - require.ErrorIs(t, err, ErrEventNotFound) - }) - - t.Run("ErrorOnInvalidEvent", func(t *testing.T) { - l := createValidLog() - l.Data = []byte{0xbb, 0xcc} - _, err := inbox.DecodeExecutingMessageLog(l) - require.ErrorIs(t, err, batching.ErrInvalidEvent) - }) -} diff --git a/op-supervisor/supervisor/backend/processors/executing_message.go b/op-supervisor/supervisor/backend/processors/executing_message.go new file mode 100644 index 000000000000..eedd8ce4b1e3 --- /dev/null +++ b/op-supervisor/supervisor/backend/processors/executing_message.go @@ -0,0 +1,38 @@ +package processors + +import ( + "fmt" + + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/interoptypes" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +type EventDecoderFn func(*ethTypes.Log) (*types.ExecutingMessage, error) + +func DecodeExecutingMessageLog(l *ethTypes.Log) (*types.ExecutingMessage, error) { + if l.Address != params.InteropCrossL2InboxAddress { + return nil, nil + } + if len(l.Topics) != 2 { // topics: event-id and payload-hash + return nil, nil + } + if l.Topics[0] != interoptypes.ExecutingMessageEventTopic { + return nil, nil + } + var msg interoptypes.Message + if err := msg.DecodeEvent(l.Topics, l.Data); err != nil { + return nil, fmt.Errorf("invalid executing message: %w", err) + } + logHash := types.PayloadHashToLogHash(msg.PayloadHash, msg.Identifier.Origin) + return &types.ExecutingMessage{ + // TODO(#11105): translate chain index to chain ID + Chain: types.ChainIndex(msg.Identifier.ChainID.Uint64()), + BlockNum: msg.Identifier.BlockNumber, + LogIdx: msg.Identifier.LogIndex, + Timestamp: msg.Identifier.Timestamp, + Hash: logHash, + }, nil +} diff --git a/op-supervisor/supervisor/backend/processors/executing_message_test.go b/op-supervisor/supervisor/backend/processors/executing_message_test.go new file mode 100644 index 000000000000..6badeb201672 --- /dev/null +++ b/op-supervisor/supervisor/backend/processors/executing_message_test.go @@ -0,0 +1,58 @@ +package processors + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +func TestDecodeExecutingMessageLog(t *testing.T) { + data := ` +{ + "address": "0x4200000000000000000000000000000000000022", + "topics": [ + "0x5c37832d2e8d10e346e55ad62071a6a2f9fa5130614ef2ec6617555c6f467ba7", + "0xc3f57e1f0dd62a4f77787d834029bfeaab8894022c47edbe13b044fb658c9190" + ], + "data": "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000000000000000000000000000000000000000119d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006724d56300000000000000000000000000000000000000000000000000000000000dbc68", + "blockHash": "0x355b82e9db9105fe3e7b7ed1897878ecbba8be7f30f94aca9dc55b6296a624cf", + "blockNumber": "0x13a8", + "transactionHash": "0x6eb22bb67562ac6a8fdbf60d6227c0b1f3f9d1d15ead1b0de055358f4fb9fa69", + "transactionIndex": "0x2", + "logIndex": "0x0", + "removed": false +} +` + var logEvent ethTypes.Log + require.NoError(t, json.Unmarshal([]byte(data), &logEvent)) + + msg, err := DecodeExecutingMessageLog(&logEvent) + require.NoError(t, err) + require.NotNil(t, msg) + + // struct Identifier { + // address origin; + // uint256 blockNumber; + // uint256 logIndex; + // uint256 timestamp; + // uint256 chainId; + // } + // function executeMessage(Identifier calldata _id, + // address _target, bytes calldata _message) external payable; + + originAddr := common.HexToAddress("0x5fbdb2315678afecb367f032d93f642f64180aa3") + payloadHash := common.HexToHash("0xc3f57e1f0dd62a4f77787d834029bfeaab8894022c47edbe13b044fb658c9190") + logHash := types.PayloadHashToLogHash(payloadHash, originAddr) + + require.Equal(t, logHash, msg.Hash) + require.Equal(t, uint64(4509), msg.BlockNum) + require.Equal(t, uint32(0), msg.LogIdx) + require.Equal(t, uint64(1730467171), msg.Timestamp) + require.Equal(t, types.ChainIndex(900200), msg.Chain) +} diff --git a/op-supervisor/supervisor/backend/processors/log_processor.go b/op-supervisor/supervisor/backend/processors/log_processor.go index e8adb29ce6bf..2ca79c4d2f2f 100644 --- a/op-supervisor/supervisor/backend/processors/log_processor.go +++ b/op-supervisor/supervisor/backend/processors/log_processor.go @@ -2,7 +2,6 @@ package processors import ( "context" - "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -10,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors/contracts" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -24,21 +22,17 @@ type ChainsDBClientForLogProcessor interface { AddLog(chain types.ChainID, logHash common.Hash, parentBlock eth.BlockID, logIdx uint32, execMsg *types.ExecutingMessage) error } -type EventDecoder interface { - DecodeExecutingMessageLog(log *ethTypes.Log) (types.ExecutingMessage, error) -} - type logProcessor struct { chain types.ChainID logStore LogStorage - eventDecoder EventDecoder + eventDecoder EventDecoderFn } func NewLogProcessor(chain types.ChainID, logStore LogStorage) LogProcessor { return &logProcessor{ chain: chain, logStore: logStore, - eventDecoder: contracts.NewCrossL2Inbox(), + eventDecoder: DecodeExecutingMessageLog, } } @@ -49,19 +43,15 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts for _, l := range rcpt.Logs { // log hash represents the hash of *this* log as a potentially initiating message logHash := logToLogHash(l) - var execMsg *types.ExecutingMessage - msg, err := p.eventDecoder.DecodeExecutingMessageLog(l) - if err != nil && !errors.Is(err, contracts.ErrEventNotFound) { - return fmt.Errorf("failed to decode executing message log: %w", err) - } else if err == nil { - // if the log is an executing message, store the message - execMsg = &msg + // The log may be an executing message emitted by the CrossL2Inbox + execMsg, err := p.eventDecoder(l) + if err != nil { + return fmt.Errorf("invalid log %d from block %s: %w", l.Index, block.ID(), err) } // executing messages have multiple entries in the database // they should start with the initiating message and then include the execution - err = p.logStore.AddLog(p.chain, logHash, block.ParentID(), uint32(l.Index), execMsg) - if err != nil { - return fmt.Errorf("failed to add log %d from block %v: %w", l.Index, block.ID(), err) + if err := p.logStore.AddLog(p.chain, logHash, block.ParentID(), uint32(l.Index), execMsg); err != nil { + return fmt.Errorf("failed to add log %d from block %s: %w", l.Index, block.ID(), err) } } } @@ -77,6 +67,6 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts // The address is hashed into the payload hash to save space in the log storage, // and because they represent paired data. func logToLogHash(l *ethTypes.Log) common.Hash { - payloadHash := crypto.Keccak256(types.LogToMessagePayload(l)) - return types.PayloadHashToLogHash(common.Hash(payloadHash), l.Address) + payloadHash := crypto.Keccak256Hash(types.LogToMessagePayload(l)) + return types.PayloadHashToLogHash(payloadHash, l.Address) } diff --git a/op-supervisor/supervisor/backend/processors/log_processor_test.go b/op-supervisor/supervisor/backend/processors/log_processor_test.go index e23ad6933f74..f919d4f801ed 100644 --- a/op-supervisor/supervisor/backend/processors/log_processor_test.go +++ b/op-supervisor/supervisor/backend/processors/log_processor_test.go @@ -107,7 +107,7 @@ func TestLogProcessor(t *testing.T) { }, }, } - execMsg := types.ExecutingMessage{ + execMsg := &types.ExecutingMessage{ Chain: 4, // TODO(#11105): translate chain ID to chain index BlockNum: 6, LogIdx: 8, @@ -116,10 +116,10 @@ func TestLogProcessor(t *testing.T) { } store := &stubLogStorage{} processor := NewLogProcessor(types.ChainID{4}, store).(*logProcessor) - processor.eventDecoder = EventDecoderFn(func(l *ethTypes.Log) (types.ExecutingMessage, error) { + processor.eventDecoder = func(l *ethTypes.Log) (*types.ExecutingMessage, error) { require.Equal(t, rcpts[0].Logs[0], l) return execMsg, nil - }) + } err := processor.ProcessLogs(ctx, block1, rcpts) require.NoError(t, err) @@ -128,7 +128,7 @@ func TestLogProcessor(t *testing.T) { parent: block1.ParentID(), logIdx: 0, logHash: logToLogHash(rcpts[0].Logs[0]), - execMsg: &execMsg, + execMsg: execMsg, }, } require.Equal(t, expected, store.logs) @@ -242,9 +242,3 @@ type storedLog struct { logHash common.Hash execMsg *types.ExecutingMessage } - -type EventDecoderFn func(*ethTypes.Log) (types.ExecutingMessage, error) - -func (f EventDecoderFn) DecodeExecutingMessageLog(log *ethTypes.Log) (types.ExecutingMessage, error) { - return f(log) -} From 20c0e146ee83e78c8d779f9d5d1a6ff18c3f6e3c Mon Sep 17 00:00:00 2001 From: Inphi Date: Fri, 1 Nov 2024 08:55:50 -0700 Subject: [PATCH 105/451] ci: Skip expensive fp e2e tests in main workflow (#12780) Co-authored-by: Matthew Slipper --- .circleci/config.yml | 6 ++++++ op-e2e/e2e.go | 7 +++++++ op-e2e/faultproofs/output_alphabet_test.go | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3a089dd72bb2..01eba4e466d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -897,6 +897,10 @@ jobs: description: Machine resource class type: string default: ethereum-optimism/latitude-1 + skip_slow_tests: + description: Indicates that slow tests should be skipped + type: boolean + default: false machine: true resource_class: <> steps: @@ -925,6 +929,7 @@ jobs: # need to explicitly set it here to prevent Cannon from running when we don't # want it to. export OP_E2E_CANNON_ENABLED="false" + export OP_E2E_SKIP_SLOW_TEST=<> # Note: We don't use circle CI test splits because we need to split by test name, not by package. There is an additional # constraint that gotestsum does not currently (nor likely will) accept files from different packages when building. JUNIT_FILE=../tmp/test-results/<>_<>.xml JSON_LOG_FILE=../tmp/testlogs/test.log make <> @@ -1362,6 +1367,7 @@ workflows: name: op-e2e-fault-proof-tests module: op-e2e target: test-fault-proofs + skip_slow_tests: true requires: - contracts-bedrock-build - cannon-prestate diff --git a/op-e2e/e2e.go b/op-e2e/e2e.go index 54533cf4fef9..f603b97f6fbd 100644 --- a/op-e2e/e2e.go +++ b/op-e2e/e2e.go @@ -52,6 +52,13 @@ func UsesCannon(t e2eutils.TestingBase) { } } +// IsSlow indicates that the test is too expensive to run on the main CI workflow +func IsSlow(t e2eutils.TestingBase) { + if os.Getenv("OP_E2E_SKIP_SLOW_TEST") == "true" { + t.Skip("Skipping slow test") + } +} + type executorInfo struct { total uint64 idx uint64 diff --git a/op-e2e/faultproofs/output_alphabet_test.go b/op-e2e/faultproofs/output_alphabet_test.go index 9255214ff39b..258066305ef3 100644 --- a/op-e2e/faultproofs/output_alphabet_test.go +++ b/op-e2e/faultproofs/output_alphabet_test.go @@ -167,7 +167,7 @@ func TestOutputAlphabetGame_ValidOutputRoot(t *testing.T) { } func TestChallengerCompleteExhaustiveDisputeGame(t *testing.T) { - op_e2e.InitParallel(t) + op_e2e.InitParallel(t, op_e2e.IsSlow) testCase := func(t *testing.T, isRootCorrect bool) { ctx := context.Background() From 69875d1fa7a0f0d82ac4e4d65b4fba68d2cf5663 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 1 Nov 2024 22:56:36 +0700 Subject: [PATCH 106/451] fix(ct): no public functions in libraries (#12777) Adds a new semgrep rule that blocks the usage of public functions in libraries. We don't use linked libraries and they cause issues with foundry. --- packages/contracts-bedrock/justfile | 4 + .../scripts/libraries/DeployUtils.sol | 2 +- semgrep/sol-rules.t.sol | 122 ++++++++++++++++++ semgrep/sol-rules.yaml | 11 ++ 4 files changed, 138 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index d77e93822462..e73673634745 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -201,6 +201,10 @@ check-kontrol-summaries-unchanged: semgrep: cd ../../ && semgrep scan --config=semgrep ./packages/contracts-bedrock +# Runs semgrep tests. +semgrep-test: + cd ../../ && semgrep scan --test semgrep + # TODO: Also run lint-forge-tests-check but we need to fix the test names first. # Runs all checks. check: diff --git a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol index 597e5b100fc3..da9aea12a0ac 100644 --- a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol +++ b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol @@ -263,7 +263,7 @@ library DeployUtils { /// @notice Builds an ERC1967 Proxy with a dummy implementation. /// @param _proxyImplName Name of the implementation contract. - function buildERC1967ProxyWithImpl(string memory _proxyImplName) public returns (IProxy genericProxy_) { + function buildERC1967ProxyWithImpl(string memory _proxyImplName) internal returns (IProxy genericProxy_) { genericProxy_ = IProxy( create1({ _name: "Proxy", diff --git a/semgrep/sol-rules.t.sol b/semgrep/sol-rules.t.sol index b2b769e144e6..8b82dce33ed0 100644 --- a/semgrep/sol-rules.t.sol +++ b/semgrep/sol-rules.t.sol @@ -154,6 +154,128 @@ contract SemgrepTest__sol_safety_natspec_semver_match { } } +library SemgrepTest__sol_safety_no_public_in_libraries { + // ok: sol-safety-no-public-in-libraries + function test() internal { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() private { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test(uint256 _value, address _addr) internal { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test(uint256 _value, address _addr) private { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() internal pure returns (uint256) { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() private pure returns (uint256) { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() internal view returns (uint256, address) { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() private view returns (uint256, address) { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() internal returns (uint256 amount_, bool success_) { + // ... + } + + // ok: sol-safety-no-public-in-libraries + function test() private returns (uint256 amount_, bool success_) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public pure { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external pure { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public view { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external view { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test(uint256 _value, address _addr) public { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test(uint256 _value, address _addr) external { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public pure returns (uint256) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external pure returns (uint256) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public view returns (uint256, address) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external view returns (uint256, address) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() public returns (uint256 amount_, bool success_) { + // ... + } + + // ruleid: sol-safety-no-public-in-libraries + function test() external returns (uint256 amount_, bool success_) { + // ... + } +} + contract SemgrepTest__sol_style_input_arg_fmt { // ok: sol-style-input-arg-fmt event Test(address indexed src, address indexed guy, uint256 wad); diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index bd9a80d57804..59e0374b26db 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -66,6 +66,17 @@ rules: include: - packages/contracts-bedrock/src + - id: sol-safety-no-public-in-libraries + languages: [generic] + severity: ERROR + message: Public functions in libraries are not allowed + patterns: + - pattern-inside: | + library $LIBRARY { + ... + } + - pattern-regex: function\s+\w+\s*\([^)]*\)\s+(?:.*\s+)?(public|external)\s+.*\{ + - id: sol-style-input-arg-fmt languages: [solidity] severity: ERROR From 39dc07942350899bc93b413a1688efd17048982f Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 1 Nov 2024 23:54:26 +0700 Subject: [PATCH 107/451] interop-devnet: monitor logs of L1 BN/VC/EL and L2 EL containers (#12783) --- interop-devnet/monitoring/promtail/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interop-devnet/monitoring/promtail/config.yaml b/interop-devnet/monitoring/promtail/config.yaml index 1f724873c2d0..60116fb463f6 100644 --- a/interop-devnet/monitoring/promtail/config.yaml +++ b/interop-devnet/monitoring/promtail/config.yaml @@ -25,7 +25,7 @@ scrape_configs: filters: - name: name # Filter logging to just our containers - values: ["op-batcher-*", "op-proposer-*", "op-node-*", "op-supervisor-*"] + values: ["op-batcher-*", "op-proposer-*", "op-node-*", "op-supervisor-*", "l1-*", "l1-bn-*", "l1-vc-*", "l2-a-*", "l2-b-*"] relabel_configs: - source_labels: ["__meta_docker_container_name"] regex: "/(.*)" From 1ebbd2911d216cf8d03a753ccc4d15d32cd893ef Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Fri, 1 Nov 2024 12:34:31 -0500 Subject: [PATCH 108/451] use RWMap for Supervisor Backend (#12785) * use RWMap for Supervisor Backend * adjust Clear method --- op-service/locks/rwmap.go | 7 + op-supervisor/supervisor/backend/backend.go | 152 ++++++------------ .../supervisor/backend/backend_test.go | 3 +- 3 files changed, 59 insertions(+), 103 deletions(-) diff --git a/op-service/locks/rwmap.go b/op-service/locks/rwmap.go index 779e3554c824..2a6badf25525 100644 --- a/op-service/locks/rwmap.go +++ b/op-service/locks/rwmap.go @@ -46,3 +46,10 @@ func (m *RWMap[K, V]) Range(f func(key K, value V) bool) { } } } + +// Clear removes all key-value pairs from the map. +func (m *RWMap[K, V]) Clear() { + m.mu.Lock() + defer m.mu.Unlock() + clear(m.inner) +} diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 37596c5d8b6b..7d368cb40f15 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "sync" "sync/atomic" "time" @@ -14,6 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/locks" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-supervisor/config" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/cross" @@ -30,11 +30,6 @@ type SupervisorBackend struct { m Metrics dataDir string - // RW lock to avoid concurrent map mutations. - // Read = any chain may be used and mutated. - // Write = set of chains is changing. - mu sync.RWMutex - // depSet is the dependency set that the backend uses to know about the chains it is indexing depSet depset.DependencySet @@ -42,21 +37,21 @@ type SupervisorBackend struct { chainDBs *db.ChainsDB // chainProcessors are notified of new unsafe blocks, and add the unsafe log events data into the events DB - chainProcessors map[types.ChainID]*processors.ChainProcessor + chainProcessors locks.RWMap[types.ChainID, *processors.ChainProcessor] // crossSafeProcessors take local-safe data and promote it to cross-safe when verified - crossSafeProcessors map[types.ChainID]*cross.Worker + crossSafeProcessors locks.RWMap[types.ChainID, *cross.Worker] // crossUnsafeProcessors take local-unsafe data and promote it to cross-unsafe when verified - crossUnsafeProcessors map[types.ChainID]*cross.Worker + crossUnsafeProcessors locks.RWMap[types.ChainID, *cross.Worker] + + // chainMetrics are used to track metrics for each chain + // they are reused for processors and databases of the same chain + chainMetrics locks.RWMap[types.ChainID, *chainMetrics] // synchronousProcessors disables background-workers, // requiring manual triggers for the backend to process anything. synchronousProcessors bool - - // chainMetrics are used to track metrics for each chain - // they are reused for processors and databases of the same chain - chainMetrics map[types.ChainID]*chainMetrics } var _ frontend.Backend = (*SupervisorBackend)(nil) @@ -74,22 +69,17 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg if err != nil { return nil, fmt.Errorf("failed to load dependency set: %w", err) } - chains := depSet.Chains() // create initial per-chain resources chainsDBs := db.NewChainsDB(logger, depSet) // create the supervisor backend super := &SupervisorBackend{ - logger: logger, - m: m, - dataDir: cfg.Datadir, - depSet: depSet, - chainDBs: chainsDBs, - chainProcessors: make(map[types.ChainID]*processors.ChainProcessor, len(chains)), - chainMetrics: make(map[types.ChainID]*chainMetrics, len(chains)), - crossUnsafeProcessors: make(map[types.ChainID]*cross.Worker, len(chains)), - crossSafeProcessors: make(map[types.ChainID]*cross.Worker, len(chains)), + logger: logger, + m: m, + dataDir: cfg.Datadir, + depSet: depSet, + chainDBs: chainsDBs, // For testing we can avoid running the processors. synchronousProcessors: cfg.SynchronousProcessors, } @@ -120,19 +110,19 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf // initialize all cross-unsafe processors for _, chainID := range chains { worker := cross.NewCrossUnsafeWorker(su.logger, chainID, su.chainDBs) - su.crossUnsafeProcessors[chainID] = worker + su.crossUnsafeProcessors.Set(chainID, worker) } // initialize all cross-safe processors for _, chainID := range chains { worker := cross.NewCrossSafeWorker(su.logger, chainID, su.chainDBs) - su.crossSafeProcessors[chainID] = worker + su.crossSafeProcessors.Set(chainID, worker) } // For each chain initialize a chain processor service, // after cross-unsafe workers are ready to receive updates for _, chainID := range chains { logProcessor := processors.NewLogProcessor(chainID, su.chainDBs) chainProcessor := processors.NewChainProcessor(su.logger, chainID, logProcessor, su.chainDBs, su.onIndexedLocalUnsafeData) - su.chainProcessors[chainID] = chainProcessor + su.chainProcessors.Set(chainID, chainProcessor) } // the config has some RPC connections to attach to the chain-processors @@ -148,31 +138,27 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf // onIndexedLocalUnsafeData is called by the event indexing workers. // This signals to cross-unsafe workers that there's data to index. func (su *SupervisorBackend) onIndexedLocalUnsafeData() { - su.mu.RLock() - defer su.mu.RUnlock() - // We signal all workers, since dependencies on a chain may be unblocked // by new data on other chains. // Busy workers don't block processing. // The signal is picked up only if the worker is running in the background. - for _, w := range su.crossUnsafeProcessors { + su.crossUnsafeProcessors.Range(func(_ types.ChainID, w *cross.Worker) bool { w.OnNewData() - } + return true + }) } // onNewLocalSafeData is called by the safety-indexing. // This signals to cross-safe workers that there's data to index. func (su *SupervisorBackend) onNewLocalSafeData() { - su.mu.RLock() - defer su.mu.RUnlock() - // We signal all workers, since dependencies on a chain may be unblocked // by new data on other chains. // Busy workers don't block processing. // The signal is picked up only if the worker is running in the background. - for _, w := range su.crossSafeProcessors { + su.crossSafeProcessors.Range(func(_ types.ChainID, w *cross.Worker) bool { w.OnNewData() - } + return true + }) } // openChainDBs initializes all the DB resources of a specific chain. @@ -180,7 +166,7 @@ func (su *SupervisorBackend) onNewLocalSafeData() { func (su *SupervisorBackend) openChainDBs(chainID types.ChainID) error { cm := newChainMetrics(chainID, su.m) // create metrics and a logdb for the chain - su.chainMetrics[chainID] = cm + su.chainMetrics.Set(chainID, cm) logDB, err := db.OpenLogDB(su.logger, chainID, su.dataDir, cm) if err != nil { @@ -216,7 +202,7 @@ func (su *SupervisorBackend) attachRPC(ctx context.Context, rpc string) error { if !su.depSet.HasChain(chainID) { return fmt.Errorf("chain %s is not part of the interop dependency set: %w", chainID, types.ErrUnknownChain) } - cm, ok := su.chainMetrics[chainID] + cm, ok := su.chainMetrics.Get(chainID) if !ok { return fmt.Errorf("failed to find metrics for chain %v", chainID) } @@ -236,10 +222,7 @@ func (su *SupervisorBackend) attachRPC(ctx context.Context, rpc string) error { } func (su *SupervisorBackend) AttachProcessorSource(chainID types.ChainID, src processors.Source) error { - su.mu.RLock() - defer su.mu.RUnlock() - - proc, ok := su.chainProcessors[chainID] + proc, ok := su.chainProcessors.Get(chainID) if !ok { return fmt.Errorf("unknown chain %s, cannot attach RPC to processor", chainID) } @@ -260,9 +243,6 @@ func clientForL2(ctx context.Context, logger log.Logger, rpc string) (client.RPC } func (su *SupervisorBackend) Start(ctx context.Context) error { - su.mu.Lock() - defer su.mu.Unlock() - // ensure we only start once if !su.started.CompareAndSwap(false, true) { return errors.New("already started") @@ -276,46 +256,49 @@ func (su *SupervisorBackend) Start(ctx context.Context) error { if !su.synchronousProcessors { // Make all the chain-processors run automatic background processing - for _, processor := range su.chainProcessors { + su.chainProcessors.Range(func(_ types.ChainID, processor *processors.ChainProcessor) bool { processor.StartBackground() - } - for _, worker := range su.crossUnsafeProcessors { + return true + }) + su.crossUnsafeProcessors.Range(func(_ types.ChainID, worker *cross.Worker) bool { worker.StartBackground() - } - for _, worker := range su.crossSafeProcessors { + return true + }) + su.crossSafeProcessors.Range(func(_ types.ChainID, worker *cross.Worker) bool { worker.StartBackground() - } + return true + }) } return nil } func (su *SupervisorBackend) Stop(ctx context.Context) error { - su.mu.Lock() - defer su.mu.Unlock() - if !su.started.CompareAndSwap(true, false) { return errAlreadyStopped } su.logger.Info("Closing supervisor backend") // close all processors - for id, processor := range su.chainProcessors { + su.chainProcessors.Range(func(id types.ChainID, processor *processors.ChainProcessor) bool { su.logger.Info("stopping chain processor", "chainID", id) processor.Close() - } - clear(su.chainProcessors) + return true + }) + su.chainProcessors.Clear() - for id, worker := range su.crossUnsafeProcessors { + su.crossUnsafeProcessors.Range(func(id types.ChainID, worker *cross.Worker) bool { su.logger.Info("stopping cross-unsafe processor", "chainID", id) worker.Close() - } - clear(su.crossUnsafeProcessors) + return true + }) + su.crossUnsafeProcessors.Clear() - for id, worker := range su.crossSafeProcessors { + su.crossSafeProcessors.Range(func(id types.ChainID, worker *cross.Worker) bool { su.logger.Info("stopping cross-safe processor", "chainID", id) worker.Close() - } - clear(su.crossSafeProcessors) + return true + }) + su.crossSafeProcessors.Clear() // close the databases return su.chainDBs.Close() @@ -323,9 +306,6 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error { // AddL2RPC attaches an RPC as the RPC for the given chain, overriding the previous RPC source, if any. func (su *SupervisorBackend) AddL2RPC(ctx context.Context, rpc string) error { - su.mu.RLock() // read-lock: we only modify an existing chain, we don't add/remove chains - defer su.mu.RUnlock() - return su.attachRPC(ctx, rpc) } @@ -340,9 +320,6 @@ func (su *SupervisorBackend) DependencySet() depset.DependencySet { // ---------------------------- func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) { - su.mu.RLock() - defer su.mu.RUnlock() - logHash := types.PayloadHashToLogHash(payloadHash, identifier.Origin) chainID := identifier.ChainID blockNum := identifier.BlockNumber @@ -365,9 +342,6 @@ func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHa func (su *SupervisorBackend) CheckMessages( messages []types.Message, minSafety types.SafetyLevel) error { - su.mu.RLock() - defer su.mu.RUnlock() - su.logger.Debug("Checking messages", "count", len(messages), "minSafety", minSafety) for _, msg := range messages { @@ -393,9 +367,6 @@ func (su *SupervisorBackend) CheckMessages( } func (su *SupervisorBackend) UnsafeView(ctx context.Context, chainID types.ChainID, unsafe types.ReferenceView) (types.ReferenceView, error) { - su.mu.RLock() - defer su.mu.RUnlock() - head, err := su.chainDBs.LocalUnsafe(chainID) if err != nil { return types.ReferenceView{}, fmt.Errorf("failed to get local-unsafe head: %w", err) @@ -414,9 +385,6 @@ func (su *SupervisorBackend) UnsafeView(ctx context.Context, chainID types.Chain } func (su *SupervisorBackend) SafeView(ctx context.Context, chainID types.ChainID, safe types.ReferenceView) (types.ReferenceView, error) { - su.mu.RLock() - defer su.mu.RUnlock() - _, localSafe, err := su.chainDBs.LocalSafe(chainID) if err != nil { return types.ReferenceView{}, fmt.Errorf("failed to get local-safe head: %w", err) @@ -435,9 +403,6 @@ func (su *SupervisorBackend) SafeView(ctx context.Context, chainID types.ChainID } func (su *SupervisorBackend) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { - su.mu.RLock() - defer su.mu.RUnlock() - v, err := su.chainDBs.Finalized(chainID) if err != nil { return eth.BlockID{}, err @@ -446,9 +411,6 @@ func (su *SupervisorBackend) Finalized(ctx context.Context, chainID types.ChainI } func (su *SupervisorBackend) CrossDerivedFrom(ctx context.Context, chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) { - su.mu.RLock() - defer su.mu.RUnlock() - v, err := su.chainDBs.CrossDerivedFromBlockRef(chainID, derived) if err != nil { return eth.BlockRef{}, err @@ -460,9 +422,7 @@ func (su *SupervisorBackend) CrossDerivedFrom(ctx context.Context, chainID types // ---------------------------- func (su *SupervisorBackend) UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error { - su.mu.RLock() - defer su.mu.RUnlock() - ch, ok := su.chainProcessors[chainID] + ch, ok := su.chainProcessors.Get(chainID) if !ok { return types.ErrUnknownChain } @@ -470,9 +430,6 @@ func (su *SupervisorBackend) UpdateLocalUnsafe(ctx context.Context, chainID type } func (su *SupervisorBackend) UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error { - su.mu.RLock() - defer su.mu.RUnlock() - err := su.chainDBs.UpdateLocalSafe(chainID, derivedFrom, lastDerived) if err != nil { return err @@ -482,9 +439,6 @@ func (su *SupervisorBackend) UpdateLocalSafe(ctx context.Context, chainID types. } func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID types.ChainID, finalized eth.BlockRef) error { - su.mu.RLock() - defer su.mu.RUnlock() - return su.chainDBs.UpdateFinalizedL1(finalized) } @@ -492,9 +446,7 @@ func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID type // ---------------------------- func (su *SupervisorBackend) SyncEvents(chainID types.ChainID) error { - su.mu.RLock() - defer su.mu.RUnlock() - ch, ok := su.chainProcessors[chainID] + ch, ok := su.chainProcessors.Get(chainID) if !ok { return types.ErrUnknownChain } @@ -503,9 +455,7 @@ func (su *SupervisorBackend) SyncEvents(chainID types.ChainID) error { } func (su *SupervisorBackend) SyncCrossUnsafe(chainID types.ChainID) error { - su.mu.RLock() - defer su.mu.RUnlock() - ch, ok := su.crossUnsafeProcessors[chainID] + ch, ok := su.crossUnsafeProcessors.Get(chainID) if !ok { return types.ErrUnknownChain } @@ -513,9 +463,7 @@ func (su *SupervisorBackend) SyncCrossUnsafe(chainID types.ChainID) error { } func (su *SupervisorBackend) SyncCrossSafe(chainID types.ChainID) error { - su.mu.RLock() - defer su.mu.RUnlock() - ch, ok := su.crossSafeProcessors[chainID] + ch, ok := su.crossSafeProcessors.Get(chainID) if !ok { return types.ErrUnknownChain } diff --git a/op-supervisor/supervisor/backend/backend_test.go b/op-supervisor/supervisor/backend/backend_test.go index 6ddfcac662b2..c104bf0bae5d 100644 --- a/op-supervisor/supervisor/backend/backend_test.go +++ b/op-supervisor/supervisor/backend/backend_test.go @@ -117,7 +117,8 @@ func TestBackendLifetime(t *testing.T) { require.NoError(t, err) // Make the processing happen, so we can rely on the new chain information, // and not run into errors for future data that isn't mocked at this time. - b.chainProcessors[chainA].ProcessToHead() + proc, _ := b.chainProcessors.Get(chainA) + proc.ProcessToHead() _, err = b.UnsafeView(context.Background(), chainA, types.ReferenceView{}) require.ErrorIs(t, err, types.ErrFuture, "still no data yet, need cross-unsafe") From 4c25686adee5485e52ce9548d614dc9be11f5111 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Fri, 1 Nov 2024 20:29:29 -0600 Subject: [PATCH 109/451] op-e2e: Fix flakiness in TestP2PFull (#12793) The ConnectedF callback was sending to a channel that was only ever read from once. This causes the test to time out under certain conditions. --- op-node/p2p/host_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/op-node/p2p/host_test.go b/op-node/p2p/host_test.go index 2bab3239e55e..85b0c9e60588 100644 --- a/op-node/p2p/host_test.go +++ b/op-node/p2p/host_test.go @@ -6,6 +6,7 @@ import ( "math/big" "net" "slices" + gosync "sync" "testing" "time" @@ -126,9 +127,12 @@ func TestP2PFull(t *testing.T) { conns := make(chan network.Conn, 1) hostA := nodeA.Host() + var once gosync.Once hostA.Network().Notify(&network.NotifyBundle{ ConnectedF: func(n network.Network, conn network.Conn) { - conns <- conn + once.Do(func() { + conns <- conn + }) }}) backend := NewP2PAPIBackend(nodeA, logA, nil) From 4872837d5b4c604103086a2c238be028d8c32871 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sat, 2 Nov 2024 01:07:28 -0600 Subject: [PATCH 110/451] op-e2e: Retry the receipts fetcher test (#12794) This is one of our flakiest tests since it's very dependent on the test runner's CPU usage. --- op-service/sources/receipts_basic_test.go | 62 +++++++++++++++-------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/op-service/sources/receipts_basic_test.go b/op-service/sources/receipts_basic_test.go index ca2f00f9aa21..ad1ed16013ad 100644 --- a/op-service/sources/receipts_basic_test.go +++ b/op-service/sources/receipts_basic_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-service/retry" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" @@ -104,32 +106,48 @@ func TestBasicRPCReceiptsFetcher_Concurrency(t *testing.T) { for _, rec := range receipts { recMap[rec.TxHash] = rec } - mrpc := new(mockRPC) - rp := NewBasicRPCReceiptsFetcher(mrpc, batchSize) - // prepare mock - var numCalls atomic.Int32 - mrpc.On("BatchCallContext", mock.Anything, mock.AnythingOfType("[]rpc.BatchElem")). - Run(func(args mock.Arguments) { - numCalls.Add(1) - els := args.Get(1).([]rpc.BatchElem) - for _, el := range els { - if el.Method == "eth_getTransactionReceipt" { - txHash := el.Args[0].(common.Hash) - // The IterativeBatchCall expects that the values are written - // to the fields of the allocated *types.Receipt. - **(el.Result.(**types.Receipt)) = *recMap[txHash] + boff := &retry.ExponentialStrategy{ + Min: 0, + Max: time.Second, + MaxJitter: 100 * time.Millisecond, + } + err := retry.Do0(context.Background(), 10, boff, func() error { + mrpc := new(mockRPC) + rp := NewBasicRPCReceiptsFetcher(mrpc, batchSize) + + // prepare mock + var numCalls atomic.Int32 + mrpc.On("BatchCallContext", mock.Anything, mock.AnythingOfType("[]rpc.BatchElem")). + Run(func(args mock.Arguments) { + numCalls.Add(1) + els := args.Get(1).([]rpc.BatchElem) + for _, el := range els { + if el.Method == "eth_getTransactionReceipt" { + txHash := el.Args[0].(common.Hash) + // The IterativeBatchCall expects that the values are written + // to the fields of the allocated *types.Receipt. + **(el.Result.(**types.Receipt)) = *recMap[txHash] + } } - } - }). - Return([]error{nil}) + }). + Return([]error{nil}) - runConcurrentFetchingTest(t, rp, numFetchers, receipts, block) + runConcurrentFetchingTest(t, rp, numFetchers, receipts, block) - mrpc.AssertExpectations(t) - finalNumCalls := int(numCalls.Load()) - require.NotZero(finalNumCalls, "BatchCallContext should have been called.") - require.Less(finalNumCalls, numFetchers*numBatchCalls, "Some IterativeBatchCalls should have been shared.") + mrpc.AssertExpectations(t) + finalNumCalls := int(numCalls.Load()) + + if finalNumCalls == 0 { + return errors.New("batchCallContext should have been called") + } + + if finalNumCalls >= numFetchers*numBatchCalls { + return errors.New("some IterativeBatchCalls should have been shared") + } + return nil + }) + require.NoError(err) } func runConcurrentFetchingTest(t *testing.T, rp ReceiptsProvider, numFetchers int, receipts types.Receipts, block *RPCBlock) { From 2e0223f26fd6845ae3625943ed664b02799a99ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 07:10:13 +0000 Subject: [PATCH 111/451] dependabot(gomod): bump github.com/kurtosis-tech/kurtosis/api/golang (#12787) Bumps [github.com/kurtosis-tech/kurtosis/api/golang](https://github.com/kurtosis-tech/kurtosis) from 1.4.0 to 1.4.1. - [Release notes](https://github.com/kurtosis-tech/kurtosis/releases) - [Changelog](https://github.com/kurtosis-tech/kurtosis/blob/main/CHANGELOG.md) - [Commits](https://github.com/kurtosis-tech/kurtosis/compare/1.4.0...1.4.1) --- updated-dependencies: - dependency-name: github.com/kurtosis-tech/kurtosis/api/golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3f61278e07c2..66c99169912c 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-leveldb v0.5.0 github.com/klauspost/compress v1.17.11 - github.com/kurtosis-tech/kurtosis/api/golang v1.4.0 + github.com/kurtosis-tech/kurtosis/api/golang v1.4.1 github.com/libp2p/go-libp2p v0.36.2 github.com/libp2p/go-libp2p-mplex v0.9.0 github.com/libp2p/go-libp2p-pubsub v0.12.0 diff --git a/go.sum b/go.sum index 9794a2c370f4..3eaeda632c7b 100644 --- a/go.sum +++ b/go.sum @@ -440,8 +440,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230818182330-1a86869414d2 h1:izciXrFyFR+ihJ7nLTOkoIX5GzBPIp8gVKlw94gIc98= github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230818182330-1a86869414d2/go.mod h1:bWSMQK3WHVTGHX9CjxPAb/LtzcmfOxID2wdzakSWQxo= -github.com/kurtosis-tech/kurtosis/api/golang v1.4.0 h1:Iczs8diUFpbl4R2JJoofu6R/ZDwBsHe0O6wot45fxgQ= -github.com/kurtosis-tech/kurtosis/api/golang v1.4.0/go.mod h1:9T22P7Vv3j5g6sbm78DxHQ4s9C4Cj3s9JjFQ7DFyYpM= +github.com/kurtosis-tech/kurtosis/api/golang v1.4.1 h1:V/T5k7t1iKgFof1cGhyLh396YKdTehUqO97AsTPDy+k= +github.com/kurtosis-tech/kurtosis/api/golang v1.4.1/go.mod h1:9T22P7Vv3j5g6sbm78DxHQ4s9C4Cj3s9JjFQ7DFyYpM= github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20230818184218-f4e3e773463b h1:hMoIM99QKcYQqsnK4AF7Lovi9ZD9ac6lZLZ5D/jx2x8= github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20230818184218-f4e3e773463b/go.mod h1:4pFdrRwDz5R+Fov2ZuTaPhAVgjA2jhGh1Izf832sX7A= github.com/kurtosis-tech/kurtosis/grpc-file-transfer/golang v0.0.0-20230803130419-099ee7a4e3dc h1:7IlEpSehmWcNXOFpNP24Cu5HQI3af7GCBQw//m+LnvQ= From 9e9effa2414b578fb9f0cbaab129b1d8fde6b6db Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Sat, 2 Nov 2024 14:43:10 +0700 Subject: [PATCH 112/451] feat: DeployDisputeGame script (#12641) Adds a new deployment script for deploying a new dispute game implementation. --- .../pkg/deployer/bootstrap/delayed_weth.go | 2 +- .../pkg/deployer/bootstrap/dispute_game.go | 216 ++++++++ op-deployer/pkg/deployer/bootstrap/flags.go | 108 ++++ op-deployer/pkg/deployer/opcm/dispute_game.go | 82 +++ .../pkg/deployer/opcm/dispute_game_test.go | 57 ++ .../scripts/deploy/DeployDisputeGame.s.sol | 495 ++++++++++++++++++ 6 files changed, 959 insertions(+), 1 deletion(-) create mode 100644 op-deployer/pkg/deployer/bootstrap/dispute_game.go create mode 100644 op-deployer/pkg/deployer/opcm/dispute_game.go create mode 100644 op-deployer/pkg/deployer/opcm/dispute_game_test.go create mode 100644 packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol diff --git a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go index 45952d19e955..1e6e10535f87 100644 --- a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go +++ b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go @@ -188,7 +188,7 @@ func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error { }, ) if err != nil { - return fmt.Errorf("error deploying implementations: %w", err) + return fmt.Errorf("error deploying DelayedWETH: %w", err) } if _, err := bcaster.Broadcast(ctx); err != nil { diff --git a/op-deployer/pkg/deployer/bootstrap/dispute_game.go b/op-deployer/pkg/deployer/bootstrap/dispute_game.go new file mode 100644 index 000000000000..65ca4a731124 --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/dispute_game.go @@ -0,0 +1,216 @@ +package bootstrap + +import ( + "context" + "crypto/ecdsa" + "fmt" + "strings" + + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" +) + +type DisputeGameConfig struct { + L1RPCUrl string + PrivateKey string + Logger log.Logger + ArtifactsLocator *artifacts2.Locator + + privateKeyECDSA *ecdsa.PrivateKey + + MinProposalSizeBytes uint64 + ChallengePeriodSeconds uint64 + MipsVersion uint8 + GameKind string + GameType uint32 + AbsolutePrestate common.Hash + MaxGameDepth uint64 + SplitDepth uint64 + ClockExtension uint64 + MaxClockDuration uint64 + DelayedWethProxy common.Address + AnchorStateRegistryProxy common.Address + L2ChainId uint64 + Proposer common.Address + Challenger common.Address +} + +func (c *DisputeGameConfig) Check() error { + if c.L1RPCUrl == "" { + return fmt.Errorf("l1RPCUrl must be specified") + } + + if c.PrivateKey == "" { + return fmt.Errorf("private key must be specified") + } + + privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x")) + if err != nil { + return fmt.Errorf("failed to parse private key: %w", err) + } + c.privateKeyECDSA = privECDSA + + if c.Logger == nil { + return fmt.Errorf("logger must be specified") + } + + if c.ArtifactsLocator == nil { + return fmt.Errorf("artifacts locator must be specified") + } + + return nil +} + +func DisputeGameCLI(cliCtx *cli.Context) error { + logCfg := oplog.ReadCLIConfig(cliCtx) + l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) + oplog.SetGlobalLogHandler(l.Handler()) + + l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) + privateKey := cliCtx.String(deployer.PrivateKeyFlagName) + artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) + artifactsLocator := new(artifacts2.Locator) + if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { + return fmt.Errorf("failed to parse artifacts URL: %w", err) + } + + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + + return DisputeGame(ctx, DisputeGameConfig{ + L1RPCUrl: l1RPCUrl, + PrivateKey: privateKey, + Logger: l, + ArtifactsLocator: artifactsLocator, + }) +} + +func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { + if err := cfg.Check(); err != nil { + return fmt.Errorf("invalid config for DisputeGame: %w", err) + } + + lgr := cfg.Logger + progressor := func(curr, total int64) { + lgr.Info("artifacts download progress", "current", curr, "total", total) + } + + artifactsFS, cleanup, err := artifacts2.Download(ctx, cfg.ArtifactsLocator, progressor) + if err != nil { + return fmt.Errorf("failed to download artifacts: %w", err) + } + defer func() { + if err := cleanup(); err != nil { + lgr.Warn("failed to clean up artifacts", "err", err) + } + }() + + l1Client, err := ethclient.Dial(cfg.L1RPCUrl) + if err != nil { + return fmt.Errorf("failed to connect to L1 RPC: %w", err) + } + + chainID, err := l1Client.ChainID(ctx) + if err != nil { + return fmt.Errorf("failed to get chain ID: %w", err) + } + chainIDU64 := chainID.Uint64() + + standardVersionsTOML, err := standard.L1VersionsDataFor(chainIDU64) + if err != nil { + return fmt.Errorf("error getting standard versions TOML: %w", err) + } + + signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) + chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) + + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: lgr, + ChainID: chainID, + Client: l1Client, + Signer: signer, + From: chainDeployer, + }) + if err != nil { + return fmt.Errorf("failed to create broadcaster: %w", err) + } + + nonce, err := l1Client.NonceAt(ctx, chainDeployer, nil) + if err != nil { + return fmt.Errorf("failed to get starting nonce: %w", err) + } + + host, err := env.DefaultScriptHost( + bcaster, + lgr, + chainDeployer, + artifactsFS, + nonce, + ) + if err != nil { + return fmt.Errorf("failed to create script host: %w", err) + } + + var release string + if cfg.ArtifactsLocator.IsTag() { + release = cfg.ArtifactsLocator.Tag + } else { + release = "dev" + } + + lgr.Info("deploying dispute game", "release", release) + + dgo, err := opcm.DeployDisputeGame( + host, + opcm.DeployDisputeGameInput{ + Release: release, + StandardVersionsToml: standardVersionsTOML, + MipsVersion: cfg.MipsVersion, + MinProposalSizeBytes: cfg.MinProposalSizeBytes, + ChallengePeriodSeconds: cfg.ChallengePeriodSeconds, + GameKind: cfg.GameKind, + GameType: cfg.GameType, + AbsolutePrestate: cfg.AbsolutePrestate, + MaxGameDepth: cfg.MaxGameDepth, + SplitDepth: cfg.SplitDepth, + ClockExtension: cfg.ClockExtension, + MaxClockDuration: cfg.MaxClockDuration, + DelayedWethProxy: cfg.DelayedWethProxy, + AnchorStateRegistryProxy: cfg.AnchorStateRegistryProxy, + L2ChainId: cfg.L2ChainId, + Proposer: cfg.Proposer, + Challenger: cfg.Challenger, + }, + ) + if err != nil { + return fmt.Errorf("error deploying dispute game: %w", err) + } + + if _, err := bcaster.Broadcast(ctx); err != nil { + return fmt.Errorf("failed to broadcast: %w", err) + } + + lgr.Info("deployed dispute game") + + if err := jsonutil.WriteJSON(dgo, ioutil.ToStdOut()); err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + return nil +} diff --git a/op-deployer/pkg/deployer/bootstrap/flags.go b/op-deployer/pkg/deployer/bootstrap/flags.go index 6fd598780df5..a7b80daf6e7f 100644 --- a/op-deployer/pkg/deployer/bootstrap/flags.go +++ b/op-deployer/pkg/deployer/bootstrap/flags.go @@ -4,6 +4,7 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-service/cliapp" + "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" ) @@ -15,6 +16,18 @@ const ( ProofMaturityDelaySecondsFlagName = "proof-maturity-delay-seconds" DisputeGameFinalityDelaySecondsFlagName = "dispute-game-finality-delay-seconds" MIPSVersionFlagName = "mips-version" + GameKindFlagName = "game-kind" + GameTypeFlagName = "game-type" + AbsolutePrestateFlagName = "absolute-prestate" + MaxGameDepthFlagName = "max-game-depth" + SplitDepthFlagName = "split-depth" + ClockExtensionFlagName = "clock-extension" + MaxClockDurationFlagName = "max-clock-duration" + DelayedWethProxyFlagName = "delayed-weth-proxy" + AnchorStateRegistryProxyFlagName = "anchor-state-registry-proxy" + L2ChainIdFlagName = "l2-chain-id" + ProposerFlagName = "proposer" + ChallengerFlagName = "challenger" ) var ( @@ -59,6 +72,74 @@ var ( EnvVars: deployer.PrefixEnvVar("MIPS_VERSION"), Value: standard.MIPSVersion, } + GameKindFlag = &cli.StringFlag{ + Name: GameKindFlagName, + Usage: "Game kind (FaultDisputeGame or PermissionedDisputeGame).", + EnvVars: deployer.PrefixEnvVar("GAME_KIND"), + Value: "FaultDisputeGame", + } + GameTypeFlag = &cli.StringFlag{ + Name: GameTypeFlagName, + Usage: "Game type (integer or fractional).", + EnvVars: deployer.PrefixEnvVar("GAME_TYPE"), + } + AbsolutePrestateFlag = &cli.StringFlag{ + Name: AbsolutePrestateFlagName, + Usage: "Absolute prestate.", + EnvVars: deployer.PrefixEnvVar("ABSOLUTE_PRESTATE"), + Value: standard.DisputeAbsolutePrestate.Hex(), + } + MaxGameDepthFlag = &cli.Uint64Flag{ + Name: MaxGameDepthFlagName, + Usage: "Max game depth.", + EnvVars: deployer.PrefixEnvVar("MAX_GAME_DEPTH"), + Value: standard.DisputeMaxGameDepth, + } + SplitDepthFlag = &cli.Uint64Flag{ + Name: SplitDepthFlagName, + Usage: "Split depth.", + EnvVars: deployer.PrefixEnvVar("SPLIT_DEPTH"), + Value: standard.DisputeSplitDepth, + } + ClockExtensionFlag = &cli.Uint64Flag{ + Name: ClockExtensionFlagName, + Usage: "Clock extension.", + EnvVars: deployer.PrefixEnvVar("CLOCK_EXTENSION"), + Value: standard.DisputeClockExtension, + } + MaxClockDurationFlag = &cli.Uint64Flag{ + Name: MaxClockDurationFlagName, + Usage: "Max clock duration.", + EnvVars: deployer.PrefixEnvVar("MAX_CLOCK_DURATION"), + Value: standard.DisputeMaxClockDuration, + } + DelayedWethProxyFlag = &cli.StringFlag{ + Name: DelayedWethProxyFlagName, + Usage: "Delayed WETH proxy.", + EnvVars: deployer.PrefixEnvVar("DELAYED_WETH_PROXY"), + } + AnchorStateRegistryProxyFlag = &cli.StringFlag{ + Name: AnchorStateRegistryProxyFlagName, + Usage: "Anchor state registry proxy.", + EnvVars: deployer.PrefixEnvVar("ANCHOR_STATE_REGISTRY_PROXY"), + } + L2ChainIdFlag = &cli.Uint64Flag{ + Name: L2ChainIdFlagName, + Usage: "L2 chain ID.", + EnvVars: deployer.PrefixEnvVar("L2_CHAIN_ID"), + } + ProposerFlag = &cli.StringFlag{ + Name: ProposerFlagName, + Usage: "Proposer address (permissioned game only).", + EnvVars: deployer.PrefixEnvVar("PROPOSER"), + Value: common.Address{}.Hex(), + } + ChallengerFlag = &cli.StringFlag{ + Name: ChallengerFlagName, + Usage: "Challenger address (permissioned game only).", + EnvVars: deployer.PrefixEnvVar("CHALLENGER"), + Value: common.Address{}.Hex(), + } ) var OPCMFlags = []cli.Flag{ @@ -79,6 +160,27 @@ var DelayedWETHFlags = []cli.Flag{ ArtifactsLocatorFlag, } +var DisputeGameFlags = []cli.Flag{ + deployer.L1RPCURLFlag, + deployer.PrivateKeyFlag, + ArtifactsLocatorFlag, + MinProposalSizeBytesFlag, + ChallengePeriodSecondsFlag, + MIPSVersionFlag, + GameKindFlag, + GameTypeFlag, + AbsolutePrestateFlag, + MaxGameDepthFlag, + SplitDepthFlag, + ClockExtensionFlag, + MaxClockDurationFlag, + DelayedWethProxyFlag, + AnchorStateRegistryProxyFlag, + L2ChainIdFlag, + ProposerFlag, + ChallengerFlag, +} + var Commands = []*cli.Command{ { Name: "opcm", @@ -92,4 +194,10 @@ var Commands = []*cli.Command{ Flags: cliapp.ProtectFlags(DelayedWETHFlags), Action: DelayedWETHCLI, }, + { + Name: "disputegame", + Usage: "Bootstrap an instance of a FaultDisputeGame or PermissionedDisputeGame.", + Flags: cliapp.ProtectFlags(DisputeGameFlags), + Action: DisputeGameCLI, + }, } diff --git a/op-deployer/pkg/deployer/opcm/dispute_game.go b/op-deployer/pkg/deployer/opcm/dispute_game.go new file mode 100644 index 000000000000..001987afaeed --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/dispute_game.go @@ -0,0 +1,82 @@ +package opcm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +type DeployDisputeGameInput struct { + Release string + StandardVersionsToml string + MipsVersion uint8 + MinProposalSizeBytes uint64 + ChallengePeriodSeconds uint64 + GameKind string + GameType uint32 + AbsolutePrestate common.Hash + MaxGameDepth uint64 + SplitDepth uint64 + ClockExtension uint64 + MaxClockDuration uint64 + DelayedWethProxy common.Address + AnchorStateRegistryProxy common.Address + L2ChainId uint64 + Proposer common.Address + Challenger common.Address +} + +func (input *DeployDisputeGameInput) InputSet() bool { + return true +} + +type DeployDisputeGameOutput struct { + DisputeGameImpl common.Address + MipsSingleton common.Address + PreimageOracleSingleton common.Address +} + +func (output *DeployDisputeGameOutput) CheckOutput(input common.Address) error { + return nil +} + +type DeployDisputeGameScript struct { + Run func(input, output common.Address) error +} + +func DeployDisputeGame( + host *script.Host, + input DeployDisputeGameInput, +) (DeployDisputeGameOutput, error) { + var output DeployDisputeGameOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress[*DeployDisputeGameInput](host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployDisputeGameInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployDisputeGameOutput](host, outputAddr, &output, + script.WithFieldSetter[*DeployDisputeGameOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployDisputeGameOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployDisputeGame" + deployScript, cleanupDeploy, err := script.WithScript[DeployDisputeGameScript](host, "DeployDisputeGame.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/opcm/dispute_game_test.go b/op-deployer/pkg/deployer/opcm/dispute_game_test.go new file mode 100644 index 000000000000..d31f94f8d83b --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/dispute_game_test.go @@ -0,0 +1,57 @@ +package opcm + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestDeployDisputeGame(t *testing.T) { + _, artifacts := testutil.LocalArtifacts(t) + + host, err := env.DefaultScriptHost( + broadcaster.NoopBroadcaster(), + testlog.Logger(t, log.LevelInfo), + common.Address{'D'}, + artifacts, + 0, + ) + require.NoError(t, err) + + standardVersionsTOML, err := standard.L1VersionsDataFor(11155111) + require.NoError(t, err) + + input := DeployDisputeGameInput{ + Release: "dev", + StandardVersionsToml: standardVersionsTOML, + MipsVersion: 1, + MinProposalSizeBytes: standard.MinProposalSizeBytes, + ChallengePeriodSeconds: standard.ChallengePeriodSeconds, + GameKind: "PermissionedDisputeGame", + GameType: 1, + AbsolutePrestate: common.Hash{'A'}, + MaxGameDepth: standard.DisputeMaxGameDepth, + SplitDepth: standard.DisputeSplitDepth, + ClockExtension: standard.DisputeClockExtension, + MaxClockDuration: standard.DisputeMaxClockDuration, + DelayedWethProxy: common.Address{'D'}, + AnchorStateRegistryProxy: common.Address{'A'}, + L2ChainId: 69, + Proposer: common.Address{'P'}, + Challenger: common.Address{'C'}, + } + + output, err := DeployDisputeGame(host, input) + require.NoError(t, err) + + require.NotEmpty(t, output.DisputeGameImpl) + require.NotEmpty(t, output.MipsSingleton) + require.NotEmpty(t, output.PreimageOracleSingleton) +} diff --git a/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol new file mode 100644 index 000000000000..51b60c6c2995 --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { Script } from "forge-std/Script.sol"; + +// Scripts +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Libraries +import { GameType, Claim, Duration } from "src/dispute/lib/Types.sol"; +import { LibString } from "@solady/utils/LibString.sol"; + +// Interfaces +import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; +import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDisputeGame.sol"; +import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; +import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; +import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; +import { IMIPS } from "src/cannon/interfaces/IMIPS.sol"; + +/// @title DeployDisputeGameInput +contract DeployDisputeGameInput is BaseDeployIO { + // Common inputs. + string internal _release; + string internal _standardVersionsToml; + + // Specify which MIPS version to use. + uint256 internal _mipsVersion; + + // All inputs required to deploy PreimageOracle. + uint256 internal _minProposalSizeBytes; + uint256 internal _challengePeriodSeconds; + + // Specify which game kind is being deployed here. + string internal _gameKind; + + // All inputs required to deploy FaultDisputeGame. + uint256 internal _gameType; + bytes32 internal _absolutePrestate; + uint256 internal _maxGameDepth; + uint256 internal _splitDepth; + uint256 internal _clockExtension; + uint256 internal _maxClockDuration; + IDelayedWETH internal _delayedWethProxy; + IAnchorStateRegistry internal _anchorStateRegistryProxy; + uint256 internal _l2ChainId; + + // Additional inputs required to deploy PermissionedDisputeGame. + address internal _proposer; + address internal _challenger; + + function set(bytes4 _sel, uint256 _value) public { + if (_sel == this.mipsVersion.selector) { + require(_value == 1 || _value == 2, "DeployDisputeGame: unknown mips version"); + _mipsVersion = _value; + } else if (_sel == this.minProposalSizeBytes.selector) { + require(_value != 0, "DeployDisputeGame: minProposalSizeBytes cannot be zero"); + _minProposalSizeBytes = _value; + } else if (_sel == this.challengePeriodSeconds.selector) { + require(_value != 0, "DeployDisputeGame: challengePeriodSeconds cannot be zero"); + _challengePeriodSeconds = _value; + } else if (_sel == this.gameType.selector) { + require(_value <= type(uint32).max, "DeployDisputeGame: gameType must fit inside uint32"); + _gameType = _value; + } else if (_sel == this.maxGameDepth.selector) { + require(_value != 0, "DeployDisputeGame: maxGameDepth cannot be zero"); + _maxGameDepth = _value; + } else if (_sel == this.splitDepth.selector) { + require(_value != 0, "DeployDisputeGame: splitDepth cannot be zero"); + _splitDepth = _value; + } else if (_sel == this.clockExtension.selector) { + require(_value <= type(uint64).max, "DeployDisputeGame: clockExtension must fit inside uint64"); + require(_value != 0, "DeployDisputeGame: clockExtension cannot be zero"); + _clockExtension = _value; + } else if (_sel == this.maxClockDuration.selector) { + require(_value <= type(uint64).max, "DeployDisputeGame: maxClockDuration must fit inside uint64"); + require(_value != 0, "DeployDisputeGame: maxClockDuration cannot be zero"); + _maxClockDuration = _value; + } else if (_sel == this.l2ChainId.selector) { + require(_value != 0, "DeployDisputeGame: l2ChainId cannot be zero"); + _l2ChainId = _value; + } else { + revert("DeployDisputeGame: unknown selector"); + } + } + + function set(bytes4 _sel, address _value) public { + if (_sel == this.delayedWethProxy.selector) { + require(_value != address(0), "DeployDisputeGame: delayedWethProxy cannot be zero address"); + _delayedWethProxy = IDelayedWETH(payable(_value)); + } else if (_sel == this.anchorStateRegistryProxy.selector) { + require(_value != address(0), "DeployDisputeGame: anchorStateRegistryProxy cannot be zero address"); + _anchorStateRegistryProxy = IAnchorStateRegistry(payable(_value)); + } else if (_sel == this.proposer.selector) { + require(_value != address(0), "DeployDisputeGame: proposer cannot be zero address"); + _proposer = _value; + } else if (_sel == this.challenger.selector) { + require(_value != address(0), "DeployDisputeGame: challenger cannot be zero address"); + _challenger = _value; + } else { + revert("DeployDisputeGame: unknown selector"); + } + } + + function set(bytes4 _sel, string memory _value) public { + if (_sel == this.gameKind.selector) { + require( + LibString.eq(_value, "FaultDisputeGame") || LibString.eq(_value, "PermissionedDisputeGame"), + "DeployDisputeGame: unknown game kind" + ); + _gameKind = _value; + } else if (_sel == this.release.selector) { + require(!LibString.eq(_value, ""), "DeployDisputeGame: release cannot be empty"); + _release = _value; + } else if (_sel == this.standardVersionsToml.selector) { + require(!LibString.eq(_value, ""), "DeployDisputeGame: standardVersionsToml cannot be empty"); + _standardVersionsToml = _value; + } else { + revert("DeployDisputeGame: unknown selector"); + } + } + + function release() public view returns (string memory) { + require(!LibString.eq(_release, ""), "DeployDisputeGame: release not set"); + return _release; + } + + function standardVersionsToml() public view returns (string memory) { + require(!LibString.eq(_standardVersionsToml, ""), "DeployDisputeGame: standardVersionsToml not set"); + return _standardVersionsToml; + } + + function mipsVersion() public view returns (uint256) { + require(_mipsVersion != 0, "DeployDisputeGame: mipsVersion not set"); + require(_mipsVersion == 1 || _mipsVersion == 2, "DeployDisputeGame: unknown mips version"); + return _mipsVersion; + } + + function minProposalSizeBytes() public view returns (uint256) { + require(_minProposalSizeBytes != 0, "DeployDisputeGame: minProposalSizeBytes not set"); + return _minProposalSizeBytes; + } + + function challengePeriodSeconds() public view returns (uint256) { + require(_challengePeriodSeconds != 0, "DeployDisputeGame: challengePeriodSeconds not set"); + return _challengePeriodSeconds; + } + + function gameKind() public view returns (string memory) { + require( + LibString.eq(_gameKind, "FaultDisputeGame") || LibString.eq(_gameKind, "PermissionedDisputeGame"), + "DeployDisputeGame: unknown game kind" + ); + return _gameKind; + } + + function gameType() public view returns (uint256) { + require(_gameType <= type(uint32).max, "DeployDisputeGame: gameType must fit inside uint32"); + return _gameType; + } + + function absolutePrestate() public view returns (bytes32) { + require(_absolutePrestate != bytes32(0), "DeployDisputeGame: absolutePrestate not set"); + return _absolutePrestate; + } + + function maxGameDepth() public view returns (uint256) { + require(_maxGameDepth != 0, "DeployDisputeGame: maxGameDepth not set"); + return _maxGameDepth; + } + + function splitDepth() public view returns (uint256) { + require(_splitDepth != 0, "DeployDisputeGame: splitDepth not set"); + return _splitDepth; + } + + function clockExtension() public view returns (uint256) { + require(_clockExtension <= type(uint64).max, "DeployDisputeGame: clockExtension must fit inside uint64"); + require(_clockExtension != 0, "DeployDisputeGame: clockExtension not set"); + return _clockExtension; + } + + function maxClockDuration() public view returns (uint256) { + require(_maxClockDuration <= type(uint64).max, "DeployDisputeGame: maxClockDuration must fit inside uint64"); + require(_maxClockDuration != 0, "DeployDisputeGame: maxClockDuration not set"); + return _maxClockDuration; + } + + function delayedWethProxy() public view returns (IDelayedWETH) { + require(address(_delayedWethProxy) != address(0), "DeployDisputeGame: delayedWethProxy not set"); + return _delayedWethProxy; + } + + function anchorStateRegistryProxy() public view returns (IAnchorStateRegistry) { + require(address(_anchorStateRegistryProxy) != address(0), "DeployDisputeGame: anchorStateRegistryProxy not set"); + return _anchorStateRegistryProxy; + } + + function l2ChainId() public view returns (uint256) { + require(_l2ChainId != 0, "DeployDisputeGame: l2ChainId not set"); + return _l2ChainId; + } + + function proposer() public view returns (address) { + if (LibString.eq(_gameKind, "FaultDisputeGame")) { + require(_proposer == address(0), "DeployDisputeGame: proposer must be empty"); + } else { + require(_proposer != address(0), "DeployDisputeGame: proposer not set"); + } + return _proposer; + } + + function challenger() public view returns (address) { + if (LibString.eq(_gameKind, "FaultDisputeGame")) { + require(_challenger == address(0), "DeployDisputeGame: challenger must be empty"); + } else { + require(_challenger != address(0), "DeployDisputeGame: challenger not set"); + } + return _challenger; + } +} + +/// @title DeployDisputeGameOutput +contract DeployDisputeGameOutput is BaseDeployIO { + // PermissionedDisputeGame is used as the type here because it has all of the same functions as + // FaultDisputeGame but with the added proposer and challenger fields. + IPermissionedDisputeGame internal _disputeGameImpl; + IMIPS internal _mipsSingleton; + IPreimageOracle internal _preimageOracleSingleton; + + function set(bytes4 _sel, address _value) public { + if (_sel == this.disputeGameImpl.selector) { + require(_value != address(0), "DeployDisputeGame: disputeGameImpl cannot be zero address"); + _disputeGameImpl = IPermissionedDisputeGame(_value); + } else if (_sel == this.mipsSingleton.selector) { + require(_value != address(0), "DeployDisputeGame: mipsSingleton cannot be zero address"); + _mipsSingleton = IMIPS(_value); + } else if (_sel == this.preimageOracleSingleton.selector) { + require(_value != address(0), "DeployDisputeGame: preimageOracleSingleton cannot be zero address"); + _preimageOracleSingleton = IPreimageOracle(_value); + } else { + revert("DeployDisputeGame: unknown selector"); + } + } + + function checkOutput(DeployDisputeGameInput _dgi) public view { + DeployUtils.assertValidContractAddress(address(_preimageOracleSingleton)); + DeployUtils.assertValidContractAddress(address(_mipsSingleton)); + DeployUtils.assertValidContractAddress(address(_disputeGameImpl)); + assertValidDeploy(_dgi); + } + + function preimageOracleSingleton() public view returns (IPreimageOracle) { + DeployUtils.assertValidContractAddress(address(_preimageOracleSingleton)); + return _preimageOracleSingleton; + } + + function mipsSingleton() public view returns (IMIPS) { + DeployUtils.assertValidContractAddress(address(_mipsSingleton)); + return _mipsSingleton; + } + + function disputeGameImpl() public view returns (IPermissionedDisputeGame) { + DeployUtils.assertValidContractAddress(address(_disputeGameImpl)); + return _disputeGameImpl; + } + + function assertValidDeploy(DeployDisputeGameInput _dgi) public view { + assertValidPreimageOracleSingleton(_dgi); + assertValidMipsSingleton(_dgi); + assertValidDisputeGameImpl(_dgi); + } + + function assertValidPreimageOracleSingleton(DeployDisputeGameInput _dgi) internal view { + IPreimageOracle oracle = preimageOracleSingleton(); + + require(oracle.minProposalSize() == _dgi.minProposalSizeBytes(), "PO-10"); + require(oracle.challengePeriod() == _dgi.challengePeriodSeconds(), "PO-20"); + } + + function assertValidMipsSingleton(DeployDisputeGameInput) internal view { + IMIPS mips = mipsSingleton(); + + require(address(mips.oracle()) == address(preimageOracleSingleton()), "MIPS-10"); + } + + function assertValidDisputeGameImpl(DeployDisputeGameInput _dgi) internal view { + IPermissionedDisputeGame game = disputeGameImpl(); + + require(game.gameType().raw() == uint32(_dgi.gameType()), "DG-10"); + require(game.maxGameDepth() == _dgi.maxGameDepth(), "DG-20"); + require(game.splitDepth() == _dgi.splitDepth(), "DG-30"); + require(game.clockExtension().raw() == uint64(_dgi.clockExtension()), "DG-40"); + require(game.maxClockDuration().raw() == uint64(_dgi.maxClockDuration()), "DG-50"); + require(game.vm() == IBigStepper(address(mipsSingleton())), "DG-60"); + require(game.weth() == _dgi.delayedWethProxy(), "DG-70"); + require(game.anchorStateRegistry() == _dgi.anchorStateRegistryProxy(), "DG-80"); + require(game.l2ChainId() == _dgi.l2ChainId(), "DG-90"); + + if (LibString.eq(_dgi.gameKind(), "PermissionedDisputeGame")) { + require(game.proposer() == _dgi.proposer(), "DG-100"); + require(game.challenger() == _dgi.challenger(), "DG-110"); + } + } +} + +/// @title DeployDisputeGame +contract DeployDisputeGame is Script { + /// We need a struct for constructor args to avoid stack-too-deep errors. + struct DisputeGameConstructorArgs { + GameType gameType; + Claim absolutePrestate; + uint256 maxGameDepth; + uint256 splitDepth; + Duration clockExtension; + Duration maxClockDuration; + IBigStepper gameVm; + IDelayedWETH delayedWethProxy; + IAnchorStateRegistry anchorStateRegistryProxy; + uint256 l2ChainId; + address proposer; + address challenger; + } + + function run(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) public { + deployPreimageOracleSingleton(_dgi, _dgo); + deployMipsSingleton(_dgi, _dgo); + deployDisputeGameImpl(_dgi, _dgo); + _dgo.checkOutput(_dgi); + } + + function deployPreimageOracleSingleton(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { + string memory release = _dgi.release(); + string memory stdVerToml = _dgi.standardVersionsToml(); + string memory contractName = "preimage_oracle"; + IPreimageOracle singleton; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + singleton = IPreimageOracle(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 minProposalSizeBytes = _dgi.minProposalSizeBytes(); + uint256 challengePeriodSeconds = _dgi.challengePeriodSeconds(); + vm.broadcast(msg.sender); + singleton = IPreimageOracle( + DeployUtils.create1({ + _name: "PreimageOracle", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IPreimageOracle.__constructor__, (minProposalSizeBytes, challengePeriodSeconds)) + ) + }) + ); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } + + vm.label(address(singleton), "PreimageOracleSingleton"); + _dgo.set(_dgo.preimageOracleSingleton.selector, address(singleton)); + } + + function deployMipsSingleton(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { + string memory release = _dgi.release(); + string memory stdVerToml = _dgi.standardVersionsToml(); + string memory contractName = "mips"; + IMIPS singleton; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + singleton = IMIPS(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 mipsVersion = _dgi.mipsVersion(); + IPreimageOracle preimageOracle = IPreimageOracle(address(_dgo.preimageOracleSingleton())); + vm.broadcast(msg.sender); + singleton = IMIPS( + DeployUtils.create1({ + _name: mipsVersion == 1 ? "MIPS" : "MIPS2", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) + }) + ); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } + + vm.label(address(singleton), "MIPSSingleton"); + _dgo.set(_dgo.mipsSingleton.selector, address(singleton)); + } + + function deployDisputeGameImpl(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { + // Shove the arguments into a struct to avoid stack-too-deep errors. + DisputeGameConstructorArgs memory args = DisputeGameConstructorArgs({ + gameType: GameType.wrap(uint32(_dgi.gameType())), + absolutePrestate: Claim.wrap(_dgi.absolutePrestate()), + maxGameDepth: _dgi.maxGameDepth(), + splitDepth: _dgi.splitDepth(), + clockExtension: Duration.wrap(uint64(_dgi.clockExtension())), + maxClockDuration: Duration.wrap(uint64(_dgi.maxClockDuration())), + gameVm: IBigStepper(address(_dgo.mipsSingleton())), + delayedWethProxy: _dgi.delayedWethProxy(), + anchorStateRegistryProxy: _dgi.anchorStateRegistryProxy(), + l2ChainId: _dgi.l2ChainId(), + proposer: _dgi.proposer(), + challenger: _dgi.challenger() + }); + + // PermissionedDisputeGame is used as the type here because it is a superset of + // FaultDisputeGame. If the user requests to deploy a FaultDisputeGame, the user will get a + // FaultDisputeGame (and not a PermissionedDisputeGame). + vm.broadcast(msg.sender); + IPermissionedDisputeGame impl; + if (LibString.eq(_dgi.gameKind(), "FaultDisputeGame")) { + impl = IPermissionedDisputeGame( + DeployUtils.create1({ + _name: "FaultDisputeGame", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + IFaultDisputeGame.__constructor__, + ( + args.gameType, + args.absolutePrestate, + args.maxGameDepth, + args.splitDepth, + args.clockExtension, + args.maxClockDuration, + args.gameVm, + args.delayedWethProxy, + args.anchorStateRegistryProxy, + args.l2ChainId + ) + ) + ) + }) + ); + } else { + impl = IPermissionedDisputeGame( + DeployUtils.create1({ + _name: "PermissionedDisputeGame", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + IPermissionedDisputeGame.__constructor__, + ( + args.gameType, + args.absolutePrestate, + args.maxGameDepth, + args.splitDepth, + args.clockExtension, + args.maxClockDuration, + args.gameVm, + args.delayedWethProxy, + args.anchorStateRegistryProxy, + args.l2ChainId, + args.proposer, + args.challenger + ) + ) + ) + }) + ); + } + + vm.label(address(impl), string.concat(_dgi.gameKind(), "Impl")); + _dgo.set(_dgo.disputeGameImpl.selector, address(impl)); + } + + // Zero address is returned if the address is not found in '_standardVersionsToml'. + function getReleaseAddress( + string memory _version, + string memory _contractName, + string memory _standardVersionsToml + ) + internal + pure + returns (address addr_) + { + string memory baseKey = string.concat('.releases["', _version, '"].', _contractName); + string memory implAddressKey = string.concat(baseKey, ".implementation_address"); + string memory addressKey = string.concat(baseKey, ".address"); + try vm.parseTomlAddress(_standardVersionsToml, implAddressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + try vm.parseTomlAddress(_standardVersionsToml, addressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + addr_ = address(0); + } + } + } + + // A release is considered a 'develop' release if it does not start with 'op-contracts'. + function isDevelopRelease(string memory _release) internal pure returns (bool) { + return !LibString.startsWith(_release, "op-contracts"); + } +} From c73832d58507d283a7ef0ee4aa5391bb32b145ab Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Sat, 2 Nov 2024 16:59:52 +0800 Subject: [PATCH 113/451] remove unnecessary assignment to `--l2-eth-rpc` (#12796) --- op-challenger/cmd/main_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index d960fa607059..1de92468306c 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -1022,7 +1022,6 @@ func addRequiredCannonArgs(args map[string]string) { args["--cannon-bin"] = cannonBin args["--cannon-server"] = cannonServer args["--cannon-prestate"] = cannonPreState - args["--l2-eth-rpc"] = l2EthRpc } func addRequiredAsteriscArgs(args map[string]string) { @@ -1030,7 +1029,6 @@ func addRequiredAsteriscArgs(args map[string]string) { args["--asterisc-bin"] = asteriscBin args["--asterisc-server"] = asteriscServer args["--asterisc-prestate"] = asteriscPreState - args["--l2-eth-rpc"] = l2EthRpc } func addRequiredAsteriscKonaArgs(args map[string]string) { @@ -1038,7 +1036,6 @@ func addRequiredAsteriscKonaArgs(args map[string]string) { args["--asterisc-bin"] = asteriscBin args["--asterisc-kona-server"] = asteriscServer args["--asterisc-kona-prestate"] = asteriscPreState - args["--l2-eth-rpc"] = l2EthRpc } func toArgList(req map[string]string) []string { From fcb462f9108b423c6bf07e39d0e876dc5321f7d7 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 4 Nov 2024 19:57:03 +1000 Subject: [PATCH 114/451] op-e2e: Add action test for op-program trace extension behaviour. (#12803) --- op-e2e/actions/proofs/helpers/env.go | 6 ++ op-e2e/actions/proofs/trace_extension_test.go | 68 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 op-e2e/actions/proofs/trace_extension_test.go diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 45e8b4d7e346..aa13eb6331a4 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -145,6 +145,12 @@ func WithL2Claim(claim common.Hash) FixtureInputParam { } } +func WithL2BlockNumber(num uint64) FixtureInputParam { + return func(f *FixtureInputs) { + f.L2BlockNumber = num + } +} + func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlockNum uint64, checkResult CheckResult, fixtureInputParams ...FixtureInputParam) { // Fetch the pre and post output roots for the fault proof. preRoot, err := env.Sequencer.RollupClient().OutputAtBlock(t.Ctx(), l2ClaimBlockNum-1) diff --git a/op-e2e/actions/proofs/trace_extension_test.go b/op-e2e/actions/proofs/trace_extension_test.go new file mode 100644 index 000000000000..7dac46594d2c --- /dev/null +++ b/op-e2e/actions/proofs/trace_extension_test.go @@ -0,0 +1,68 @@ +package proofs + +import ( + "testing" + + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func runSafeHeadTraceExtensionTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg()) + + // Build an empty block on L2 + env.Sequencer.ActL2StartBlock(t) + env.Sequencer.ActL2EndBlock(t) + + // Instruct the batcher to submit the block to L1, and include the transaction. + env.Batcher.ActSubmitAll(t) + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l1Head := env.Miner.L1Chain().CurrentBlock() + l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock() + + // Ensure there is only 1 block on L1. + require.Equal(t, uint64(1), l1Head.Number.Uint64()) + // Ensure the block is marked as safe before we attempt to fault prove it. + require.Equal(t, uint64(1), l2SafeHead.Number.Uint64()) + + // Set claimed L2 block number to be past the actual safe head (still using the safe head output as the claim) + params := []helpers.FixtureInputParam{helpers.WithL2BlockNumber(l2SafeHead.Number.Uint64() + 1)} + params = append(params, testCfg.InputParams...) + env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64(), testCfg.CheckResult, params...) +} + +// Test_ProgramAction_SafeHeadTraceExtension checks that op-program correctly handles the trace extension case where +// the claimed l2 block number is after the safe head. The honest actor should repeat the output root from the safe head +// and op-program should consider it valid even though the claimed l2 block number is not reached. +// Output roots other than from the safe head should be invalid if the claimed l2 block number is not reached. +func Test_ProgramAction_SafeHeadTraceExtension(gt *testing.T) { + matrix := helpers.NewMatrix[any]() + defer matrix.Run(gt) + + matrix.AddTestCase( + "HonestClaim", + nil, + helpers.LatestForkOnly, + runSafeHeadTraceExtensionTest, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + "JunkClaim", + nil, + helpers.LatestForkOnly, + runSafeHeadTraceExtensionTest, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) +} From 5cc83a8e2780bdeaa8e5699f86311e70d1e32bcb Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 4 Nov 2024 20:12:14 +1000 Subject: [PATCH 115/451] op-program: Run until all events are exhausted instead of running until the derivation is idle (#12804) * op-e2e: Add action test for op-program trace extension behaviour. * op-program: Run until all events are exhausted instead of running until the derivation is idle. --- op-program/client/driver/driver.go | 5 ++--- op-program/client/driver/driver_test.go | 5 +++-- op-program/client/driver/program.go | 4 ---- op-program/client/driver/program_test.go | 4 ++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/op-program/client/driver/driver.go b/op-program/client/driver/driver.go index fc232c5fcd43..56fffb5b155c 100644 --- a/op-program/client/driver/driver.go +++ b/op-program/client/driver/driver.go @@ -73,15 +73,14 @@ func (d *Driver) Emit(ev event.Event) { d.events = append(d.events, ev) } -var ExhaustErr = errors.New("exhausted events before completing program") - func (d *Driver) RunComplete() error { // Initial reset d.Emit(engine.ResetEngineRequestEvent{}) for !d.end.Closing() { if len(d.events) == 0 { - return ExhaustErr + d.logger.Info("Derivation complete: no further data to process") + return d.end.Result() } if len(d.events) > 10000 { // sanity check, in case of bugs. Better than going OOM. return errors.New("way too many events queued up, something is wrong") diff --git a/op-program/client/driver/driver_test.go b/op-program/client/driver/driver_test.go index 2f1249aff6a4..8a5d02fe83f9 100644 --- a/op-program/client/driver/driver_test.go +++ b/op-program/client/driver/driver_test.go @@ -92,7 +92,8 @@ func TestDriver(t *testing.T) { } count += 1 }) - require.ErrorIs(t, ExhaustErr, d.RunComplete()) + // No further processing to be done so evaluate if the claims output root is correct. + require.NoError(t, d.RunComplete()) }) t.Run("queued events", func(t *testing.T) { @@ -104,7 +105,7 @@ func TestDriver(t *testing.T) { } count += 1 }) - require.ErrorIs(t, ExhaustErr, d.RunComplete()) + require.NoError(t, d.RunComplete()) // add 1 for initial event that RunComplete fires require.Equal(t, 1+3*2, count, "must have queued up 2 events 3 times") }) diff --git a/op-program/client/driver/program.go b/op-program/client/driver/program.go index 0ef36f8f6a47..cb3cd65542cc 100644 --- a/op-program/client/driver/program.go +++ b/op-program/client/driver/program.go @@ -62,10 +62,6 @@ func (d *ProgramDeriver) OnEvent(ev event.Event) bool { d.logger.Info("Derivation complete: reached L2 block", "head", x.SafeL2Head) d.closing = true } - case derive.DeriverIdleEvent: - // Not enough data to reach target - d.closing = true - d.logger.Info("Derivation complete: no further data to process") case rollup.ResetEvent: d.closing = true d.result = fmt.Errorf("unexpected reset error: %w", x.Err) diff --git a/op-program/client/driver/program_test.go b/op-program/client/driver/program_test.go index 4c9941d754b4..59206050a7f8 100644 --- a/op-program/client/driver/program_test.go +++ b/op-program/client/driver/program_test.go @@ -104,12 +104,12 @@ func TestProgramDeriver(t *testing.T) { require.NoError(t, p.result) }) }) - // on exhaustion of input data: stop without error + // Do not stop processing when the deriver is idle, the engine may still be busy and create further events. t.Run("deriver idle", func(t *testing.T) { p, m := newProgram(t, 1000) p.OnEvent(derive.DeriverIdleEvent{}) m.AssertExpectations(t) - require.True(t, p.closing) + require.False(t, p.closing) require.Nil(t, p.result) }) // on inconsistent chain data: stop with error From afe849ea0b0f5ee5889e0fe440ad7827d7ceef5f Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 4 Nov 2024 18:06:41 +0700 Subject: [PATCH 116/451] op-e2e/actions: Add Holocene FP action tests (#12520) * Add Holocene action tests * fix invalid batch tests * Handle rpc.Errors directly instead of relying on eth.InputErrors The fault proof program's L2 Engine API doesn't return eth.InputErrors, like the sources engine client, but directly returns rpc.Errors. So instead of relying on this translation, derivers need to deal directly with rpc.Errors. * In TryBackupUnsafeReorg, only reset on InvalidForkchoiceState error code * Add logs * include genesis FPP tests --------- Co-authored-by: Sebastian Stammler --- op-e2e/actions/helpers/l2_batcher.go | 100 +++++--- op-e2e/actions/helpers/l2_engine.go | 21 ++ op-e2e/actions/helpers/l2_sequencer.go | 17 +- op-e2e/actions/proofs/bad_tx_in_batch_test.go | 6 +- op-e2e/actions/proofs/helpers/env.go | 7 +- .../actions/proofs/holocene_batches_test.go | 142 +++++++++++ op-e2e/actions/proofs/holocene_frame_test.go | 156 ++++++++++++ .../proofs/holocene_invalid_batch_test.go | 236 ++++++++++++++++++ op-e2e/actions/proofs/l1_lookback_test.go | 3 +- op-e2e/actions/sync/sync_test.go | 4 +- op-e2e/actions/upgrades/holocene_fork_test.go | 3 +- op-e2e/opgeth/op_geth_test.go | 22 +- op-node/rollup/derive/attributes.go | 12 +- op-node/rollup/engine/build_cancel.go | 7 +- op-node/rollup/engine/build_seal.go | 6 +- op-node/rollup/engine/engine_controller.go | 33 +-- op-node/rollup/engine/engine_update.go | 13 +- op-node/rollup/engine/events.go | 3 + op-program/client/driver/program.go | 4 + .../client/l2/engineapi/l2_engine_api.go | 2 +- op-service/eth/types.go | 8 +- op-service/eth/types_test.go | 5 + op-service/sources/engine_client.go | 48 +--- 23 files changed, 742 insertions(+), 116 deletions(-) create mode 100644 op-e2e/actions/proofs/holocene_batches_test.go create mode 100644 op-e2e/actions/proofs/holocene_frame_test.go create mode 100644 op-e2e/actions/proofs/holocene_invalid_batch_test.go diff --git a/op-e2e/actions/helpers/l2_batcher.go b/op-e2e/actions/helpers/l2_batcher.go index a2121f77ca54..9fc9971a26e2 100644 --- a/op-e2e/actions/helpers/l2_batcher.go +++ b/op-e2e/actions/helpers/l2_batcher.go @@ -150,7 +150,48 @@ func (s *L2Batcher) ActL2BatchBuffer(t Testing, opts ...BlockModifier) { require.NoError(t, s.Buffer(t, opts...), "failed to add block to channel") } -type BlockModifier = func(block *types.Block) +// ActCreateChannel creates a channel if we don't have one yet. +func (s *L2Batcher) ActCreateChannel(t Testing, useSpanChannelOut bool) { + var err error + if s.L2ChannelOut == nil { + var ch ChannelOutIface + if s.l2BatcherCfg.GarbageCfg != nil { + ch, err = NewGarbageChannelOut(s.l2BatcherCfg.GarbageCfg) + } else { + target := batcher.MaxDataSize(1, s.l2BatcherCfg.MaxL1TxSize) + c, e := compressor.NewShadowCompressor(compressor.Config{ + TargetOutputSize: target, + CompressionAlgo: derive.Zlib, + }) + require.NoError(t, e, "failed to create compressor") + + if s.l2BatcherCfg.ForceSubmitSingularBatch && s.l2BatcherCfg.ForceSubmitSpanBatch { + t.Fatalf("ForceSubmitSingularBatch and ForceSubmitSpanBatch cannot be set to true at the same time") + } else { + chainSpec := rollup.NewChainSpec(s.rollupCfg) + // use span batch if we're forcing it or if we're at/beyond delta + if s.l2BatcherCfg.ForceSubmitSpanBatch || useSpanChannelOut { + ch, err = derive.NewSpanChannelOut(target, derive.Zlib, chainSpec) + // use singular batches in all other cases + } else { + ch, err = derive.NewSingularChannelOut(c, chainSpec) + } + } + } + require.NoError(t, err, "failed to create channel") + s.L2ChannelOut = ch + } +} + +type BlockModifier = func(block *types.Block) *types.Block + +func BlockLogger(t e2eutils.TestingBase) BlockModifier { + f := func(block *types.Block) *types.Block { + t.Log("added block", "num", block.Number(), "txs", block.Transactions(), "time", block.Time()) + return block + } + return f +} func (s *L2Batcher) Buffer(t Testing, opts ...BlockModifier) error { if s.l2Submitting { // break ongoing submitting work if necessary @@ -197,38 +238,13 @@ func (s *L2Batcher) Buffer(t Testing, opts ...BlockModifier) error { // Apply modifications to the block for _, f := range opts { - f(block) + if f != nil { + block = f(block) + } } - // Create channel if we don't have one yet - if s.L2ChannelOut == nil { - var ch ChannelOutIface - if s.l2BatcherCfg.GarbageCfg != nil { - ch, err = NewGarbageChannelOut(s.l2BatcherCfg.GarbageCfg) - } else { - target := batcher.MaxDataSize(1, s.l2BatcherCfg.MaxL1TxSize) - c, e := compressor.NewShadowCompressor(compressor.Config{ - TargetOutputSize: target, - CompressionAlgo: derive.Zlib, - }) - require.NoError(t, e, "failed to create compressor") + s.ActCreateChannel(t, s.rollupCfg.IsDelta(block.Time())) - if s.l2BatcherCfg.ForceSubmitSingularBatch && s.l2BatcherCfg.ForceSubmitSpanBatch { - t.Fatalf("ForceSubmitSingularBatch and ForceSubmitSpanBatch cannot be set to true at the same time") - } else { - chainSpec := rollup.NewChainSpec(s.rollupCfg) - // use span batch if we're forcing it or if we're at/beyond delta - if s.l2BatcherCfg.ForceSubmitSpanBatch || s.rollupCfg.IsDelta(block.Time()) { - ch, err = derive.NewSpanChannelOut(target, derive.Zlib, chainSpec) - // use singular batches in all other cases - } else { - ch, err = derive.NewSingularChannelOut(c, chainSpec) - } - } - } - require.NoError(t, err, "failed to create channel") - s.L2ChannelOut = ch - } if _, err := s.L2ChannelOut.AddBlock(s.rollupCfg, block); err != nil { return err } @@ -238,6 +254,30 @@ func (s *L2Batcher) Buffer(t Testing, opts ...BlockModifier) error { return nil } +// ActAddBlockByNumber causes the batcher to pull the block with the provided +// number, and add it to its ChannelOut. +func (s *L2Batcher) ActAddBlockByNumber(t Testing, blockNumber int64, opts ...BlockModifier) { + block, err := s.l2.BlockByNumber(t.Ctx(), big.NewInt(blockNumber)) + require.NoError(t, err) + require.NotNil(t, block) + + // cache block hash before we modify the block + blockHash := block.Hash() + + // Apply modifications to the block + for _, f := range opts { + if f != nil { + block = f(block) + } + } + + _, err = s.L2ChannelOut.AddBlock(s.rollupCfg, block) + require.NoError(t, err) + ref, err := s.engCl.L2BlockRefByHash(t.Ctx(), blockHash) + require.NoError(t, err, "failed to get L2BlockRef") + s.L2BufferedBlock = ref +} + func (s *L2Batcher) ActL2ChannelClose(t Testing) { // Don't run this action if there's no data to submit if s.L2ChannelOut == nil { diff --git a/op-e2e/actions/helpers/l2_engine.go b/op-e2e/actions/helpers/l2_engine.go index 963a18769e6c..8d2f318a08dc 100644 --- a/op-e2e/actions/helpers/l2_engine.go +++ b/op-e2e/actions/helpers/l2_engine.go @@ -191,9 +191,30 @@ func (e *L2Engine) ActL2RPCFail(t Testing, err error) { } } +// ActL2IncludeTx includes the next transaction from the given address in the block that is being built, +// skipping the usual check for e.EngineApi.ForcedEmpty() +func (e *L2Engine) ActL2IncludeTxIgnoreForcedEmpty(from common.Address) Action { + return func(t Testing) { + if e.EngineApi.ForcedEmpty() { + e.log.Info("Ignoring e.L2ForceEmpty=true") + } + + tx := firstValidTx(t, from, e.EngineApi.PendingIndices, e.Eth.TxPool().ContentFrom, e.EthClient().NonceAt) + err := e.EngineApi.IncludeTx(tx, from) + if errors.Is(err, engineapi.ErrNotBuildingBlock) { + t.InvalidAction(err.Error()) + } else if errors.Is(err, engineapi.ErrUsesTooMuchGas) { + t.InvalidAction("included tx uses too much gas: %v", err) + } else if err != nil { + require.NoError(t, err, "include tx") + } + } +} + // ActL2IncludeTx includes the next transaction from the given address in the block that is being built func (e *L2Engine) ActL2IncludeTx(from common.Address) Action { return func(t Testing) { + if e.EngineApi.ForcedEmpty() { e.log.Info("Skipping including a transaction because e.L2ForceEmpty is true") return diff --git a/op-e2e/actions/helpers/l2_sequencer.go b/op-e2e/actions/helpers/l2_sequencer.go index 23fe7b0686f8..daf267839581 100644 --- a/op-e2e/actions/helpers/l2_sequencer.go +++ b/op-e2e/actions/helpers/l2_sequencer.go @@ -43,7 +43,8 @@ func (m *MockL1OriginSelector) FindL1Origin(ctx context.Context, l2Head eth.L2Bl type L2Sequencer struct { *L2Verifier - sequencer *sequencing.Sequencer + sequencer *sequencing.Sequencer + attrBuilder *derive.FetchingAttributesBuilder failL2GossipUnsafeBlock error // mock error @@ -85,6 +86,7 @@ func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, blobSrc deri return &L2Sequencer{ L2Verifier: ver, sequencer: seq, + attrBuilder: attrBuilder, mockL1OriginSelector: l1OriginSelector, failL2GossipUnsafeBlock: nil, } @@ -139,12 +141,23 @@ func (s *L2Sequencer) ActL2EmptyBlock(t Testing) { // ActL2KeepL1Origin makes the sequencer use the current L1 origin, even if the next origin is available. func (s *L2Sequencer) ActL2KeepL1Origin(t Testing) { parent := s.engine.UnsafeL2Head() - // force old origin, for testing purposes + // force old origin oldOrigin, err := s.l1.L1BlockRefByHash(t.Ctx(), parent.L1Origin.Hash) require.NoError(t, err, "failed to get current origin: %s", parent.L1Origin) s.mockL1OriginSelector.originOverride = oldOrigin } +// ActL2ForceAdvanceL1Origin forces the sequencer to advance the current L1 origin, even if the next origin's timestamp is too new. +func (s *L2Sequencer) ActL2ForceAdvanceL1Origin(t Testing) { + s.attrBuilder.TestSkipL1OriginCheck() // skip check in attributes builder + parent := s.engine.UnsafeL2Head() + // force next origin + nextNum := parent.L1Origin.Number + 1 + nextOrigin, err := s.l1.L1BlockRefByNumber(t.Ctx(), nextNum) + require.NoError(t, err, "failed to get next origin by number: %d", nextNum) + s.mockL1OriginSelector.originOverride = nextOrigin +} + // ActBuildToL1Head builds empty blocks until (incl.) the L1 head becomes the L2 origin func (s *L2Sequencer) ActBuildToL1Head(t Testing) { for s.engine.UnsafeL2Head().L1Origin.Number < s.syncStatus.L1Head().Number { diff --git a/op-e2e/actions/proofs/bad_tx_in_batch_test.go b/op-e2e/actions/proofs/bad_tx_in_batch_test.go index b750e8f9e649..ce6e37469c1b 100644 --- a/op-e2e/actions/proofs/bad_tx_in_batch_test.go +++ b/op-e2e/actions/proofs/bad_tx_in_batch_test.go @@ -26,12 +26,13 @@ func runBadTxInBatchTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { env.Alice.L2.ActCheckReceiptStatusOfLastTx(true)(t) // Instruct the batcher to submit a faulty channel, with an invalid tx. - env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) { + env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) *types.Block { // Replace the tx with one that has a bad signature. txs := block.Transactions() newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) txs[1] = newTx require.NoError(t, err) + return block }) env.Batcher.ActL2ChannelClose(t) env.Batcher.ActL2BatchSubmit(t) @@ -90,12 +91,13 @@ func runBadTxInBatch_ResubmitBadFirstFrame_Test(gt *testing.T, testCfg *helpers. // Instruct the batcher to submit a faulty channel, with an invalid tx in the second block // within the span batch. env.Batcher.ActL2BatchBuffer(t) - err := env.Batcher.Buffer(t, func(block *types.Block) { + err := env.Batcher.Buffer(t, func(block *types.Block) *types.Block { // Replace the tx with one that has a bad signature. txs := block.Transactions() newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) txs[1] = newTx require.NoError(t, err) + return block }) require.NoError(t, err) env.Batcher.ActL2ChannelClose(t) diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index aa13eb6331a4..8ba29a6101ea 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -153,7 +153,12 @@ func WithL2BlockNumber(num uint64) FixtureInputParam { func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlockNum uint64, checkResult CheckResult, fixtureInputParams ...FixtureInputParam) { // Fetch the pre and post output roots for the fault proof. - preRoot, err := env.Sequencer.RollupClient().OutputAtBlock(t.Ctx(), l2ClaimBlockNum-1) + l2PreBlockNum := l2ClaimBlockNum - 1 + if l2ClaimBlockNum == 0 { + // If we are at genesis, we assert that we don't move the chain at all. + l2PreBlockNum = 0 + } + preRoot, err := env.Sequencer.RollupClient().OutputAtBlock(t.Ctx(), l2PreBlockNum) require.NoError(t, err) claimRoot, err := env.Sequencer.RollupClient().OutputAtBlock(t.Ctx(), l2ClaimBlockNum) require.NoError(t, err) diff --git a/op-e2e/actions/proofs/holocene_batches_test.go b/op-e2e/actions/proofs/holocene_batches_test.go new file mode 100644 index 000000000000..b67d2bd9da6d --- /dev/null +++ b/op-e2e/actions/proofs/holocene_batches_test.go @@ -0,0 +1,142 @@ +package proofs + +import ( + "fmt" + "testing" + + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func Test_ProgramAction_HoloceneBatches(gt *testing.T) { + type testCase struct { + name string + blocks []uint // blocks is an ordered list of blocks (by number) to add to a single channel. + isSpanBatch bool + holoceneExpectations + } + + // Depending on the blocks list, we expect a different + // progression of the safe head under Holocene + // derivation rules, compared with pre Holocene. + testCases := []testCase{ + // Standard channel composition + { + name: "case-0", blocks: []uint{1, 2, 3}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, + safeHeadHolocene: 3, + }, + }, + + // Non-standard channel composition + { + name: "case-2a", blocks: []uint{1, 3, 2}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // batches are buffered, so the block ordering does not matter + safeHeadHolocene: 1, // batch for block 3 is considered invalid because it is from the future. This batch + remaining channel is dropped. + }, + }, + { + name: "case-2b", blocks: []uint{2, 1, 3}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // batches are buffered, so the block ordering does not matter + safeHeadHolocene: 0, // batch for block 2 is considered invalid because it is from the future. This batch + remaining channel is dropped. + }, + }, + + { + name: "case-2c", blocks: []uint{1, 1, 2, 3}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // duplicate batches are silently dropped, so this reduceds to case-0 + safeHeadHolocene: 3, // duplicate batches are silently dropped + }, + }, + { + name: "case-2d", blocks: []uint{2, 2, 1, 3}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // duplicate batches are silently dropped, so this reduces to case-2b + safeHeadHolocene: 0, // duplicate batches are silently dropped, so this reduces to case-2b + }, + }, + } + + runHoloceneDerivationTest := func(gt *testing.T, testCfg *helpers.TestCfg[testCase]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg()) + + includeBatchTx := func() { + // Include the last transaction submitted by the batcher. + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + } + + max := func(input []uint) uint { + max := uint(0) + for _, val := range input { + if val > max { + max = val + } + } + return max + } + + targetHeadNumber := max(testCfg.Custom.blocks) + for env.Engine.L2Chain().CurrentBlock().Number.Uint64() < uint64(targetHeadNumber) { + env.Sequencer.ActL2StartBlock(t) + // Send an L2 tx + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTx(env.Alice.Address())(t) + env.Sequencer.ActL2EndBlock(t) + } + + // Buffer the blocks in the batcher. + env.Batcher.ActCreateChannel(t, testCfg.Custom.isSpanBatch) + for _, blockNum := range testCfg.Custom.blocks { + env.Batcher.ActAddBlockByNumber(t, int64(blockNum), actionsHelpers.BlockLogger(t)) + } + env.Batcher.ActL2ChannelClose(t) + frame := env.Batcher.ReadNextOutputFrame(t) + require.NotEmpty(t, frame) + env.Batcher.ActL2BatchSubmitRaw(t, frame) + includeBatchTx() + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l2SafeHead := env.Sequencer.L2Safe() + testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) + + t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) + + env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } + + matrix := helpers.NewMatrix[testCase]() + defer matrix.Run(gt) + + for _, ordering := range testCases { + matrix.AddTestCase( + fmt.Sprintf("HonestClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + fmt.Sprintf("JunkClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) + } +} diff --git a/op-e2e/actions/proofs/holocene_frame_test.go b/op-e2e/actions/proofs/holocene_frame_test.go new file mode 100644 index 000000000000..5093527b758d --- /dev/null +++ b/op-e2e/actions/proofs/holocene_frame_test.go @@ -0,0 +1,156 @@ +package proofs + +import ( + "fmt" + "testing" + + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +type holoceneExpectations struct { + safeHeadPreHolocene uint64 + safeHeadHolocene uint64 +} + +func (h holoceneExpectations) RequireExpectedProgress(t actionsHelpers.StatefulTesting, actualSafeHead eth.L2BlockRef, isHolocene bool, engine *actionsHelpers.L2Engine) { + if isHolocene { + require.Equal(t, h.safeHeadPreHolocene, actualSafeHead.Number) + expectedHash := engine.L2Chain().GetBlockByNumber(h.safeHeadPreHolocene).Hash() + require.Equal(t, expectedHash, actualSafeHead.Hash) + } else { + require.Equal(t, h.safeHeadHolocene, actualSafeHead.Number) + expectedHash := engine.L2Chain().GetBlockByNumber(h.safeHeadHolocene).Hash() + require.Equal(t, expectedHash, actualSafeHead.Hash) + } +} + +func Test_ProgramAction_HoloceneFrames(gt *testing.T) { + type testCase struct { + name string + frames []uint + holoceneExpectations + } + + // An ordered list of frames to read from the channel and submit + // on L1. We expect a different progression of the safe head under Holocene + // derivation rules, compared with pre Holocene. + testCases := []testCase{ + // Standard frame submission, + { + name: "case-0", frames: []uint{0, 1, 2}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, + safeHeadHolocene: 3, + }, + }, + + // Non-standard frame submission + { + name: "case-1a", frames: []uint{2, 1, 0}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter + safeHeadHolocene: 0, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. + }, + }, + { + name: "case-1b", frames: []uint{0, 1, 0, 2}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter + safeHeadHolocene: 0, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. + }, + }, + { + name: "case-1c", frames: []uint{0, 1, 1, 2}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter + safeHeadHolocene: 3, // non-contiguous frames are dropped. So this reduces to case-0. + }, + }, + } + + runHoloceneDerivationTest := func(gt *testing.T, testCfg *helpers.TestCfg[testCase]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg()) + + blocks := []uint{1, 2, 3} + targetHeadNumber := 3 + for env.Engine.L2Chain().CurrentBlock().Number.Uint64() < uint64(targetHeadNumber) { + env.Sequencer.ActL2StartBlock(t) + // Send an L2 tx + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTx(env.Alice.Address())(t) + env.Sequencer.ActL2EndBlock(t) + } + + // Build up a local list of frames + orderedFrames := make([][]byte, 0, len(testCfg.Custom.frames)) + // Buffer the blocks in the batcher and populat orderedFrames list + env.Batcher.ActCreateChannel(t, false) + for i, blockNum := range blocks { + env.Batcher.ActAddBlockByNumber(t, int64(blockNum), actionsHelpers.BlockLogger(t)) + if i == len(blocks)-1 { + env.Batcher.ActL2ChannelClose(t) + } + frame := env.Batcher.ReadNextOutputFrame(t) + require.NotEmpty(t, frame, "frame %d", i) + orderedFrames = append(orderedFrames, frame) + } + + includeBatchTx := func() { + // Include the last transaction submitted by the batcher. + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + + // Finalize the block with the first channel frame on L1. + env.Miner.ActL1SafeNext(t) + env.Miner.ActL1FinalizeNext(t) + } + + // Submit frames in specified order order + for _, j := range testCfg.Custom.frames { + env.Batcher.ActL2BatchSubmitRaw(t, orderedFrames[j]) + includeBatchTx() + } + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l2SafeHead := env.Sequencer.L2Safe() + + testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) + + t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) + + env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } + + matrix := helpers.NewMatrix[testCase]() + defer matrix.Run(gt) + + for _, ordering := range testCases { + matrix.AddTestCase( + fmt.Sprintf("HonestClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + fmt.Sprintf("JunkClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) + } +} diff --git a/op-e2e/actions/proofs/holocene_invalid_batch_test.go b/op-e2e/actions/proofs/holocene_invalid_batch_test.go new file mode 100644 index 000000000000..3e210ab72ee4 --- /dev/null +++ b/op-e2e/actions/proofs/holocene_invalid_batch_test.go @@ -0,0 +1,236 @@ +package proofs + +import ( + "fmt" + "math/big" + "testing" + + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" +) + +func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { + type testCase struct { + name string + blocks []uint // An ordered list of blocks (by number) to add to a single channel. + useSpanBatch bool + blockModifiers []actionsHelpers.BlockModifier + breachMaxSequencerDrift bool + overAdvanceL1Origin int // block number at which to over-advance + holoceneExpectations + } + + // invalidPayload invalidates the signature for the second transaction in the block. + // This should result in an invalid payload in the engine queue. + invalidPayload := func(block *types.Block) *types.Block { + alice := types.NewCancunSigner(big.NewInt(901)) + txs := block.Transactions() + newTx, err := txs[1].WithSignature(alice, make([]byte, 65)) + if err != nil { + panic(err) + } + txs[1] = newTx + return block + } + + // invalidParentHash invalidates the parentHash of the block. + // This should result in an invalid batch being derived, + // but only for singular (not for span) batches. + invalidParentHash := func(block *types.Block) *types.Block { + headerCopy := block.Header() + headerCopy.ParentHash = common.MaxHash + return block.WithSeal(headerCopy) + } + + k := 2000 + twoThousandBlocks := make([]uint, k) + for i := 0; i < k; i++ { + twoThousandBlocks[i] = uint(i) + 1 + } + + // Depending on the blocks list, whether the channel is built as + // as span batch channel, and whether the blocks are modified / invalidated + // we expect a different progression of the safe head under Holocene + // derivation rules, compared with pre Holocene. + testCases := []testCase{ + // Standard frame submission, standard channel composition + { + name: "valid", blocks: []uint{1, 2, 3}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 3, safeHeadHolocene: 3, + }, + }, + + { + name: "invalid-payload", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidPayload, nil}, + useSpanBatch: false, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 1, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. + safeHeadHolocene: 2, // We expect the safe head to move to 2 due to creation of an deposit-only block. + }, + }, + { + name: "invalid-payload-span", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidPayload, nil}, + useSpanBatch: true, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 0, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. + safeHeadHolocene: 2, // We expect the safe head to move to 2 due to creation of an deposit-only block. + }, + }, + + { + name: "invalid-parent-hash", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidParentHash, nil}, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 1, // Invalid parentHash in block 2 causes an invalid batch to be dropped. + safeHeadHolocene: 1, // Same with Holocene. + }, + }, + { + name: "seq-drift-span", blocks: twoThousandBlocks, // if we artificially stall the l1 origin, this should be enough to trigger violation of the max sequencer drift + useSpanBatch: true, + breachMaxSequencerDrift: true, + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 0, // Entire span batch invalidated. + safeHeadHolocene: 1800, // We expect partial validity until we hit sequencer drift. + }, + }, + { + name: "future-l1-origin-span", + blocks: []uint{1, 2, 3, 4}, + useSpanBatch: true, + overAdvanceL1Origin: 3, // this will over-advance the L1 origin of block 3 + holoceneExpectations: holoceneExpectations{ + safeHeadPreHolocene: 0, // Entire span batch invalidated. + safeHeadHolocene: 2, // We expect partial validity, safe head should move to block 2, dropping invalid block 3 and remaining channel. + }, + }, + } + + runHoloceneDerivationTest := func(gt *testing.T, testCfg *helpers.TestCfg[testCase]) { + t := actionsHelpers.NewDefaultTesting(gt) + tp := helpers.NewTestParams(func(tp *e2eutils.TestParams) { + // Set the channel timeout to 10 blocks, 12x lower than the sequencing window. + tp.ChannelTimeout = 10 + }) + + env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg()) + + includeBatchTx := func() { + // Include the last transaction submitted by the batcher. + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + // Finalize the block with the first channel frame on L1. + env.Miner.ActL1SafeNext(t) + env.Miner.ActL1FinalizeNext(t) + } + + env.Batcher.ActCreateChannel(t, testCfg.Custom.useSpanBatch) + + max := func(input []uint) uint { + max := uint(0) + for _, val := range input { + if val > max { + max = val + } + } + return max + } + + if testCfg.Custom.overAdvanceL1Origin > 0 { + // Generate future L1 origin or we cannot advance to it. + env.Miner.ActEmptyBlock(t) + } + + targetHeadNumber := max(testCfg.Custom.blocks) + for env.Engine.L2Chain().CurrentBlock().Number.Uint64() < uint64(targetHeadNumber) { + parentNum := env.Engine.L2Chain().CurrentBlock().Number.Uint64() + + if testCfg.Custom.breachMaxSequencerDrift { + // prevent L1 origin from progressing + env.Sequencer.ActL2KeepL1Origin(t) + } else if oa := testCfg.Custom.overAdvanceL1Origin; oa > 0 && oa == int(parentNum)+1 { + env.Sequencer.ActL2ForceAdvanceL1Origin(t) + } + + env.Sequencer.ActL2StartBlock(t) + + if !testCfg.Custom.breachMaxSequencerDrift { + // Send an L2 tx + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTx(env.Alice.Address())(t) + } + + if testCfg.Custom.breachMaxSequencerDrift && + parentNum == 1799 || + parentNum == 1800 || + parentNum == 1801 { + // Send an L2 tx and force sequencer to include it + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTxIgnoreForcedEmpty(env.Alice.Address())(t) + } + + env.Sequencer.ActL2EndBlock(t) + } + + // Buffer the blocks in the batcher. + for i, blockNum := range testCfg.Custom.blocks { + var blockModifier actionsHelpers.BlockModifier + if len(testCfg.Custom.blockModifiers) > i { + blockModifier = testCfg.Custom.blockModifiers[i] + } + env.Batcher.ActAddBlockByNumber(t, int64(blockNum), blockModifier, actionsHelpers.BlockLogger(t)) + + } + + env.Batcher.ActL2ChannelClose(t) + frame := env.Batcher.ReadNextOutputFrame(t) + require.NotEmpty(t, frame) + env.Batcher.ActL2BatchSubmitRaw(t, frame) + includeBatchTx() + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l2SafeHead := env.Sequencer.L2Safe() + + testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) + + t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) + + if safeHeadNumber := l2SafeHead.Number; safeHeadNumber > 0 { + env.RunFaultProofProgram(t, safeHeadNumber, testCfg.CheckResult, testCfg.InputParams...) + } + } + + matrix := helpers.NewMatrix[testCase]() + defer matrix.Run(gt) + + for _, ordering := range testCases { + matrix.AddTestCase( + fmt.Sprintf("HonestClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + fmt.Sprintf("JunkClaim-%s", ordering.name), + ordering, + helpers.NewForkMatrix(helpers.Granite, helpers.LatestFork), + runHoloceneDerivationTest, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) + } +} diff --git a/op-e2e/actions/proofs/l1_lookback_test.go b/op-e2e/actions/proofs/l1_lookback_test.go index b40635ac5509..3e9c6fdcd52b 100644 --- a/op-e2e/actions/proofs/l1_lookback_test.go +++ b/op-e2e/actions/proofs/l1_lookback_test.go @@ -73,11 +73,12 @@ func runL1LookbackTest_ReopenChannel(gt *testing.T, testCfg *helpers.TestCfg[any env.Miner.ActL1SafeNext(t) // Re-submit the first L2 block frame w/ different transaction data. - err := env.Batcher.Buffer(t, func(block *types.Block) { + err := env.Batcher.Buffer(t, func(block *types.Block) *types.Block { env.Bob.L2.ActResetTxOpts(t) env.Bob.L2.ActSetTxToAddr(&env.Dp.Addresses.Mallory) tx := env.Bob.L2.MakeTransaction(t) block.Transactions()[1] = tx + return block }) require.NoError(t, err) env.Batcher.ActL2BatchSubmit(t) diff --git a/op-e2e/actions/sync/sync_test.go b/op-e2e/actions/sync/sync_test.go index 95f55d63fcdd..b9c29404dca5 100644 --- a/op-e2e/actions/sync/sync_test.go +++ b/op-e2e/actions/sync/sync_test.go @@ -618,8 +618,8 @@ func TestBackupUnsafeReorgForkChoiceNotInputError(gt *testing.T) { // check pendingSafe is reset require.Equal(t, sequencer.L2PendingSafe().Number, uint64(0)) // check backupUnsafe is applied - require.Equal(t, sequencer.L2Unsafe().Hash, targetUnsafeHeadHash) - require.Equal(t, sequencer.L2Unsafe().Number, uint64(5)) + require.Equal(t, uint64(5), sequencer.L2Unsafe().Number) + require.Equal(t, targetUnsafeHeadHash, sequencer.L2Unsafe().Hash) // safe head cannot be advanced because batch contained invalid blocks require.Equal(t, sequencer.L2Safe().Number, uint64(0)) } diff --git a/op-e2e/actions/upgrades/holocene_fork_test.go b/op-e2e/actions/upgrades/holocene_fork_test.go index 7fd46296aaa9..12c0dcde6d19 100644 --- a/op-e2e/actions/upgrades/holocene_fork_test.go +++ b/op-e2e/actions/upgrades/holocene_fork_test.go @@ -170,12 +170,13 @@ func TestHoloceneInvalidPayload(gt *testing.T) { require.Len(t, b.Transactions(), 2) // buffer into the batcher, invalidating the tx via signature zeroing - env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) { + env.Batcher.ActL2BatchBuffer(t, func(block *types.Block) *types.Block { // Replace the tx with one that has a bad signature. txs := block.Transactions() newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) require.NoError(t, err) txs[1] = newTx + return block }) // generate two more empty blocks diff --git a/op-e2e/opgeth/op_geth_test.go b/op-e2e/opgeth/op_geth_test.go index 0f27e7b7f047..dcc18440f917 100644 --- a/op-e2e/opgeth/op_geth_test.go +++ b/op-e2e/opgeth/op_geth_test.go @@ -7,13 +7,9 @@ import ( "testing" "time" - op_e2e "github.com/ethereum-optimism/optimism/op-e2e" - - "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" - "github.com/ethereum-optimism/optimism/op-node/rollup" - "github.com/ethereum-optimism/optimism/op-node/rollup/derive" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -22,8 +18,13 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/rpc" + + op_e2e "github.com/ethereum-optimism/optimism/op-e2e" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" ) var ( @@ -51,8 +52,9 @@ func TestMissingGasLimit(t *testing.T) { res, err := opGeth.StartBlockBuilding(ctx, attrs) require.Error(t, err) - require.ErrorIs(t, err, eth.InputError{}) - require.Equal(t, eth.InvalidPayloadAttributes, err.(eth.InputError).Code) + var rpcErr rpc.Error + require.ErrorAs(t, err, &rpcErr) + require.EqualValues(t, eth.InvalidPayloadAttributes, rpcErr.ErrorCode()) require.Nil(t, res) } diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index e17888201bae..5d859a9fcd59 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -28,6 +28,8 @@ type FetchingAttributesBuilder struct { rollupCfg *rollup.Config l1 L1ReceiptsFetcher l2 SystemConfigL2Fetcher + // whether to skip the L1 origin timestamp check - only for testing purposes + testSkipL1OriginCheck bool } func NewFetchingAttributesBuilder(rollupCfg *rollup.Config, l1 L1ReceiptsFetcher, l2 SystemConfigL2Fetcher) *FetchingAttributesBuilder { @@ -38,6 +40,12 @@ func NewFetchingAttributesBuilder(rollupCfg *rollup.Config, l1 L1ReceiptsFetcher } } +// TestSkipL1OriginCheck skips the L1 origin timestamp check for testing purposes. +// Must not be used in production! +func (ba *FetchingAttributesBuilder) TestSkipL1OriginCheck() { + ba.testSkipL1OriginCheck = true +} + // PreparePayloadAttributes prepares a PayloadAttributes template that is ready to build a L2 block with deposits only, on top of the given l2Parent, with the given epoch as L1 origin. // The template defaults to NoTxPool=true, and no sequencer transactions: the caller has to modify the template to add transactions, // by setting NoTxPool=false as sequencer, or by appending batch transactions as verifier. @@ -93,9 +101,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex seqNumber = l2Parent.SequenceNumber + 1 } - // Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2 nextL2Time := l2Parent.Time + ba.rollupCfg.BlockTime - if nextL2Time < l1Info.Time() { + // Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2 + if !ba.testSkipL1OriginCheck && nextL2Time < l1Info.Time() { return nil, NewResetError(fmt.Errorf("cannot build L2 block on top %s for time %d before L1 origin %s at time %d", l2Parent, nextL2Time, eth.ToBlockID(l1Info), l1Info.Time())) } diff --git a/op-node/rollup/engine/build_cancel.go b/op-node/rollup/engine/build_cancel.go index 7c9995c28e85..4215e2d6c7f8 100644 --- a/op-node/rollup/engine/build_cancel.go +++ b/op-node/rollup/engine/build_cancel.go @@ -2,6 +2,9 @@ package engine import ( "context" + "errors" + + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -23,7 +26,9 @@ func (eq *EngDeriver) onBuildCancel(ev BuildCancelEvent) { eq.log.Warn("cancelling old block building job", "info", ev.Info) _, err := eq.ec.engine.GetPayload(ctx, ev.Info) if err != nil { - if x, ok := err.(eth.InputError); ok && x.Code == eth.UnknownPayload { //nolint:all + var rpcErr rpc.Error + if errors.As(err, &rpcErr) && eth.ErrorCode(rpcErr.ErrorCode()) == eth.UnknownPayload { + eq.log.Warn("tried cancelling unknown block building job", "info", ev.Info, "err", err) return // if unknown, then it did not need to be cancelled anymore. } eq.log.Error("failed to cancel block building job", "info", ev.Info, "err", err) diff --git a/op-node/rollup/engine/build_seal.go b/op-node/rollup/engine/build_seal.go index 8c4e5baeeec7..b292681e13f1 100644 --- a/op-node/rollup/engine/build_seal.go +++ b/op-node/rollup/engine/build_seal.go @@ -2,9 +2,12 @@ package engine import ( "context" + "errors" "fmt" "time" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -58,7 +61,8 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { sealingStart := time.Now() envelope, err := eq.ec.engine.GetPayload(ctx, ev.Info) if err != nil { - if x, ok := err.(eth.InputError); ok && x.Code == eth.UnknownPayload { //nolint:all + var rpcErr rpc.Error + if errors.As(err, &rpcErr) && eth.ErrorCode(rpcErr.ErrorCode()) == eth.UnknownPayload { eq.log.Warn("Cannot seal block, payload ID is unknown", "payloadID", ev.Info.ID, "payload_time", ev.Info.Timestamp, "started_time", ev.BuildStarted) diff --git a/op-node/rollup/engine/engine_controller.go b/op-node/rollup/engine/engine_controller.go index 4ac8b88a50cf..b088e382f07f 100644 --- a/op-node/rollup/engine/engine_controller.go +++ b/op-node/rollup/engine/engine_controller.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" @@ -86,7 +87,8 @@ type EngineController struct { } func NewEngineController(engine ExecEngine, log log.Logger, metrics derive.Metrics, - rollupCfg *rollup.Config, syncCfg *sync.Config, emitter event.Emitter) *EngineController { + rollupCfg *rollup.Config, syncCfg *sync.Config, emitter event.Emitter, +) *EngineController { syncStatus := syncStatusCL if syncCfg.SyncMode == sync.ELSync { syncStatus = syncStatusWillStartEL @@ -283,11 +285,11 @@ func (e *EngineController) TryUpdateEngine(ctx context.Context) error { defer logFn() fcRes, err := e.engine.ForkchoiceUpdate(ctx, &fc, nil) if err != nil { - var inputErr eth.InputError - if errors.As(err, &inputErr) { - switch inputErr.Code { + var rpcErr rpc.Error + if errors.As(err, &rpcErr) { + switch eth.ErrorCode(rpcErr.ErrorCode()) { case eth.InvalidForkchoiceState: - return derive.NewResetError(fmt.Errorf("forkchoice update was inconsistent with engine, need reset to resolve: %w", inputErr.Unwrap())) + return derive.NewResetError(fmt.Errorf("forkchoice update was inconsistent with engine, need reset to resolve: %w", rpcErr)) default: return derive.NewTemporaryError(fmt.Errorf("unexpected error code in forkchoice-updated response: %w", err)) } @@ -361,11 +363,11 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et defer logFn() fcRes, err := e.engine.ForkchoiceUpdate(ctx, &fc, nil) if err != nil { - var inputErr eth.InputError - if errors.As(err, &inputErr) { - switch inputErr.Code { + var rpcErr rpc.Error + if errors.As(err, &rpcErr) { + switch eth.ErrorCode(rpcErr.ErrorCode()) { case eth.InvalidForkchoiceState: - return derive.NewResetError(fmt.Errorf("pre-unsafe-block forkchoice update was inconsistent with engine, need reset to resolve: %w", inputErr.Unwrap())) + return derive.NewResetError(fmt.Errorf("pre-unsafe-block forkchoice update was inconsistent with engine, need reset to resolve: %w", rpcErr)) default: return derive.NewTemporaryError(fmt.Errorf("unexpected error code in forkchoice-updated response: %w", err)) } @@ -439,13 +441,16 @@ func (e *EngineController) TryBackupUnsafeReorg(ctx context.Context) (bool, erro defer logFn() fcRes, err := e.engine.ForkchoiceUpdate(ctx, &fc, nil) if err != nil { - var inputErr eth.InputError - if errors.As(err, &inputErr) { - e.SetBackupUnsafeL2Head(eth.L2BlockRef{}, false) - switch inputErr.Code { + var rpcErr rpc.Error + if errors.As(err, &rpcErr) { + switch eth.ErrorCode(rpcErr.ErrorCode()) { case eth.InvalidForkchoiceState: - return true, derive.NewResetError(fmt.Errorf("forkchoice update was inconsistent with engine, need reset to resolve: %w", inputErr.Unwrap())) + e.SetBackupUnsafeL2Head(eth.L2BlockRef{}, false) + return true, derive.NewResetError(fmt.Errorf("forkchoice update was inconsistent with engine, need reset to resolve: %w", rpcErr)) default: + // Retry when forkChoiceUpdate returns non-input error. + // Do not reset backupUnsafeHead because it will be used again. + e.needFCUCallForBackupUnsafeReorg = true return true, derive.NewTemporaryError(fmt.Errorf("unexpected error code in forkchoice-updated response: %w", err)) } } else { diff --git a/op-node/rollup/engine/engine_update.go b/op-node/rollup/engine/engine_update.go index c79dbdcd0f4d..dbf578d30376 100644 --- a/op-node/rollup/engine/engine_update.go +++ b/op-node/rollup/engine/engine_update.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" ) // isDepositTx checks an opaqueTx to determine if it is a Deposit Transaction @@ -84,15 +85,15 @@ const ( func startPayload(ctx context.Context, eng ExecEngine, fc eth.ForkchoiceState, attrs *eth.PayloadAttributes) (id eth.PayloadID, errType BlockInsertionErrType, err error) { fcRes, err := eng.ForkchoiceUpdate(ctx, &fc, attrs) if err != nil { - var inputErr eth.InputError - if errors.As(err, &inputErr) { - switch inputErr.Code { + var rpcErr rpc.Error + if errors.As(err, &rpcErr) { + switch code := eth.ErrorCode(rpcErr.ErrorCode()); code { case eth.InvalidForkchoiceState: - return eth.PayloadID{}, BlockInsertPrestateErr, fmt.Errorf("pre-block-creation forkchoice update was inconsistent with engine, need reset to resolve: %w", inputErr.Unwrap()) + return eth.PayloadID{}, BlockInsertPrestateErr, fmt.Errorf("pre-block-creation forkchoice update was inconsistent with engine, need reset to resolve: %w", rpcErr) case eth.InvalidPayloadAttributes: - return eth.PayloadID{}, BlockInsertPayloadErr, fmt.Errorf("payload attributes are not valid, cannot build block: %w", inputErr.Unwrap()) + return eth.PayloadID{}, BlockInsertPayloadErr, fmt.Errorf("payload attributes are not valid, cannot build block: %w", rpcErr) default: - if inputErr.Code.IsEngineError() { + if code.IsEngineError() { return eth.PayloadID{}, BlockInsertPrestateErr, fmt.Errorf("unexpected engine error code in forkchoice-updated response: %w", err) } else { return eth.PayloadID{}, BlockInsertTemporaryErr, fmt.Errorf("unexpected generic error code in forkchoice-updated response: %w", err) diff --git a/op-node/rollup/engine/events.go b/op-node/rollup/engine/events.go index bb1dcb66200d..bb4499564875 100644 --- a/op-node/rollup/engine/events.go +++ b/op-node/rollup/engine/events.go @@ -401,6 +401,7 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { // Only promote if not already stale. // Resets/overwrites happen through engine-resets, not through promotion. if x.Ref.Number > d.ec.PendingSafeL2Head().Number { + d.log.Debug("Updating pending safe", "pending_safe", x.Ref, "local_safe", d.ec.LocalSafeL2Head(), "unsafe", d.ec.UnsafeL2Head(), "concluding", x.Concluding) d.ec.SetPendingSafeL2Head(x.Ref) d.emitter.Emit(PendingSafeUpdateEvent{ PendingSafe: d.ec.PendingSafeL2Head(), @@ -419,6 +420,7 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { DerivedFrom: x.DerivedFrom, }) case PromoteLocalSafeEvent: + d.log.Debug("Updating local safe", "local_safe", x.Ref, "safe", d.ec.SafeL2Head(), "unsafe", d.ec.UnsafeL2Head()) d.ec.SetLocalSafeHead(x.Ref) d.emitter.Emit(LocalSafeUpdateEvent(x)) case LocalSafeUpdateEvent: @@ -427,6 +429,7 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { d.emitter.Emit(PromoteSafeEvent(x)) } case PromoteSafeEvent: + d.log.Debug("Updating safe", "safe", x.Ref, "unsafe", d.ec.UnsafeL2Head()) d.ec.SetSafeHead(x.Ref) // Finalizer can pick up this safe cross-block now d.emitter.Emit(SafeDerivedEvent{Safe: x.Ref, DerivedFrom: x.DerivedFrom}) diff --git a/op-program/client/driver/program.go b/op-program/client/driver/program.go index cb3cd65542cc..b83a1566c8ce 100644 --- a/op-program/client/driver/program.go +++ b/op-program/client/driver/program.go @@ -62,6 +62,10 @@ func (d *ProgramDeriver) OnEvent(ev event.Event) bool { d.logger.Info("Derivation complete: reached L2 block", "head", x.SafeL2Head) d.closing = true } + case derive.DeriverIdleEvent: + // We dont't close the deriver yet, as the engine may still be processing events to reach + // the target. A ForkchoiceUpdateEvent will close the deriver when the target is reached. + d.logger.Info("Derivation complete: no further L1 data to process") case rollup.ResetEvent: d.closing = true d.result = fmt.Errorf("unexpected reset error: %w", x.Err) diff --git a/op-program/client/l2/engineapi/l2_engine_api.go b/op-program/client/l2/engineapi/l2_engine_api.go index 13910d971c3a..273483893b37 100644 --- a/op-program/client/l2/engineapi/l2_engine_api.go +++ b/op-program/client/l2/engineapi/l2_engine_api.go @@ -130,9 +130,9 @@ func (ea *L2EngineAPI) IncludeTx(tx *types.Transaction, from common.Address) err if ea.blockProcessor == nil { return ErrNotBuildingBlock } + if ea.l2ForceEmpty { ea.log.Info("Skipping including a transaction because e.L2ForceEmpty is true") - // t.InvalidAction("cannot include any sequencer txs") return nil } diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 60eb7171d651..7b4d1c053348 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -38,8 +38,7 @@ const ( var ErrBedrockScalarPaddingNotEmpty = errors.New("version 0 scalar value has non-empty padding") -// InputError distinguishes an user-input error from regular rpc errors, -// to help the (Engine) API user divert from accidental input mistakes. +// InputError can be used to create rpc.Error instances with a specific error code. type InputError struct { Inner error Code ErrorCode @@ -49,6 +48,11 @@ func (ie InputError) Error() string { return fmt.Sprintf("input error %d: %s", ie.Code, ie.Inner.Error()) } +// Makes InputError implement the rpc.Error interface +func (ie InputError) ErrorCode() int { + return int(ie.Code) +} + func (ie InputError) Unwrap() error { return ie.Inner } diff --git a/op-service/eth/types_test.go b/op-service/eth/types_test.go index 36aae514be48..03409e16008d 100644 --- a/op-service/eth/types_test.go +++ b/op-service/eth/types_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/require" ) @@ -21,6 +22,10 @@ func TestInputError(t *testing.T) { t.Fatalf("need InputError to be detected as such") } require.ErrorIs(t, err, InputError{}, "need to detect input error with errors.Is") + + var rpcErr rpc.Error + require.ErrorAs(t, err, &rpcErr, "need input error to be rpc.Error with errors.As") + require.EqualValues(t, err.Code, rpcErr.ErrorCode()) } type scalarTest struct { diff --git a/op-service/sources/engine_client.go b/op-service/sources/engine_client.go index 79921192f9e7..66b6521065f9 100644 --- a/op-service/sources/engine_client.go +++ b/op-service/sources/engine_client.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/client" @@ -86,11 +85,7 @@ func (s *EngineAPIClient) EngineVersionProvider() EngineVersionProvider { return // ForkchoiceUpdate updates the forkchoice on the execution client. If attributes is not nil, the engine client will also begin building a block // based on attributes after the new head block and return the payload ID. -// -// The RPC may return three types of errors: -// 1. Processing error: ForkchoiceUpdatedResult.PayloadStatusV1.ValidationError or other non-success PayloadStatusV1, -// 2. `error` as eth.InputError: the forkchoice state or attributes are not valid. -// 3. Other types of `error`: temporary RPC errors, like timeouts. +// It's the caller's responsibility to check the error type, and in case of an rpc.Error, check the ErrorCode. func (s *EngineAPIClient) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState, attributes *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) { llog := s.log.New("state", fc) // local logger tlog := llog.New("attr", attributes) // trace logger @@ -100,28 +95,15 @@ func (s *EngineAPIClient) ForkchoiceUpdate(ctx context.Context, fc *eth.Forkchoi var result eth.ForkchoiceUpdatedResult method := s.evp.ForkchoiceUpdatedVersion(attributes) err := s.RPC.CallContext(fcCtx, &result, string(method), fc, attributes) - if err == nil { - tlog.Trace("Shared forkchoice-updated signal") - if attributes != nil { // block building is optional, we only get a payload ID if we are building a block - tlog.Trace("Received payload id", "payloadId", result.PayloadID) - } - return &result, nil - } else { + if err != nil { llog.Warn("Failed to share forkchoice-updated signal", "err", err) - if rpcErr, ok := err.(rpc.Error); ok { - code := eth.ErrorCode(rpcErr.ErrorCode()) - switch code { - case eth.InvalidParams, eth.InvalidForkchoiceState, eth.InvalidPayloadAttributes: - return nil, eth.InputError{ - Inner: err, - Code: code, - } - default: - return nil, fmt.Errorf("unrecognized rpc error: %w", err) - } - } return nil, err } + tlog.Trace("Shared forkchoice-updated signal") + if attributes != nil { // block building is optional, we only get a payload ID if we are building a block + tlog.Trace("Received payload id", "payloadId", result.PayloadID) + } + return &result, nil } // NewPayload executes a full block on the execution engine. @@ -154,9 +136,7 @@ func (s *EngineAPIClient) NewPayload(ctx context.Context, payload *eth.Execution } // GetPayload gets the execution payload associated with the PayloadId. -// There may be two types of error: -// 1. `error` as eth.InputError: the payload ID may be unknown -// 2. Other types of `error`: temporary RPC errors, like timeouts. +// It's the caller's responsibility to check the error type, and in case of an rpc.Error, check the ErrorCode. func (s *EngineAPIClient) GetPayload(ctx context.Context, payloadInfo eth.PayloadInfo) (*eth.ExecutionPayloadEnvelope, error) { e := s.log.New("payload_id", payloadInfo.ID) e.Trace("getting payload") @@ -165,18 +145,6 @@ func (s *EngineAPIClient) GetPayload(ctx context.Context, payloadInfo eth.Payloa err := s.RPC.CallContext(ctx, &result, string(method), payloadInfo.ID) if err != nil { e.Warn("Failed to get payload", "payload_id", payloadInfo.ID, "err", err) - if rpcErr, ok := err.(rpc.Error); ok { - code := eth.ErrorCode(rpcErr.ErrorCode()) - switch code { - case eth.UnknownPayload: - return nil, eth.InputError{ - Inner: err, - Code: code, - } - default: - return nil, fmt.Errorf("unrecognized rpc error: %w", err) - } - } return nil, err } e.Trace("Received payload") From 99d637a3fb495732bba554384668dc509fd9f9ae Mon Sep 17 00:00:00 2001 From: DenseDenise Date: Mon, 4 Nov 2024 12:06:22 +0000 Subject: [PATCH 117/451] docs: Unified input parameter names (#12797) * Change the name * Update op-challenger/README.md Co-authored-by: smartcontracts --------- Co-authored-by: smartcontracts --- op-challenger/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-challenger/README.md b/op-challenger/README.md index 3274dc47d808..97fafab8e6f7 100644 --- a/op-challenger/README.md +++ b/op-challenger/README.md @@ -187,7 +187,7 @@ Prints the list of current claims in a dispute game. ```shell ./bin/op-challenger run-trace \ - --network \ + --network= \ --l1-eth-rpc= \ --l1-beacon= \ --l2-eth-rpc= \ @@ -197,7 +197,7 @@ Prints the list of current claims in a dispute game. --run= ``` -* `PREDEFINED_NETWORK` - the name of a predefined L2 network. +* `NETWORK_NAME` - the name of a predefined L2 network. * `L1_ETH_RPC` - the RPC endpoint of the L1 endpoint to use (e.g. `http://localhost:8545`). * `L1_BEACON` - the REST endpoint of the L1 beacon node to use (e.g. `http://localhost:5100`). * `L2_ETH_RPC` - the RPC endpoint of the L2 execution client to use From 0c5617983d4ba1c8b083759749198e710271a09a Mon Sep 17 00:00:00 2001 From: Delweng Date: Mon, 4 Nov 2024 21:04:21 +0800 Subject: [PATCH 118/451] feat(semgrep): add sol-style-force-require-msg (#12806) * feat(semgrep): add sol-force-require-msg rule Signed-off-by: jsvisa * feat(semgrep): exclude packages temporary Signed-off-by: jsvisa * feat(semgrep): add testcase Signed-off-by: jsvisa * test script example Signed-off-by: jsvisa * require msg more clear Signed-off-by: jsvisa --------- Signed-off-by: jsvisa --- .../script/testdata/scripts/ScriptExample.s.sol | 4 ++-- semgrep/sol-rules.t.sol | 13 ++++++++++--- semgrep/sol-rules.yaml | 11 +++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol index 84b9c0381da4..f9ea6239d433 100644 --- a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol +++ b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol @@ -124,12 +124,12 @@ contract ScriptExample { console.log("contract deployment"); vm.broadcast(address(uint160(0x123456))); FooBar x = new FooBar(1234); - require(x.foo() == 1234); + require(x.foo() == 1234, "FooBar: foo in create is not 1234"); console.log("create 2"); vm.broadcast(address(uint160(0xcafe))); FooBar y = new FooBar{salt: bytes32(uint256(42))}(1234); - require(y.foo() == 1234); + require(y.foo() == 1234, "FooBar: foo in create2 is not 1234"); console.log("done!"); // Deploy a script without a pranked sender and check the nonce. diff --git a/semgrep/sol-rules.t.sol b/semgrep/sol-rules.t.sol index 8b82dce33ed0..3b4329f7e388 100644 --- a/semgrep/sol-rules.t.sol +++ b/semgrep/sol-rules.t.sol @@ -483,9 +483,6 @@ contract SemgrepTest__sol_style_malformed_require { // ok: sol-style-malformed-require require(cond, "CHECK-L2OO-140"); - // ok: sol-style-malformed-require - require(cond); - // ok: sol-style-malformed-require require(bytes(env_).length > 0, "Config: must set DEPLOY_CONFIG_PATH to filesystem path of deploy config"); @@ -551,3 +548,13 @@ contract SemgrepTest__sol_style_malformed_revert { revert("test"); } } + +contract SemgrepTest__sol_style_enforce_require_msg { + function test() { + // ok: sol-style-enforce-require-msg + require(cond, "MyContract: test message good"); + + // ruleid: sol-style-enforce-require-msg + require(cond); + } +} diff --git a/semgrep/sol-rules.yaml b/semgrep/sol-rules.yaml index 59e0374b26db..e0d83414c4bd 100644 --- a/semgrep/sol-rules.yaml +++ b/semgrep/sol-rules.yaml @@ -175,3 +175,14 @@ rules: - packages/contracts-bedrock/src/L1/OPContractsManager.sol - packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol - packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol + + - id: sol-style-enforce-require-msg + languages: [solidity] + severity: ERROR + message: Require statement must have an error message + patterns: + - pattern: require($ERR); + - pattern-not: require($ERR, $MSG); + paths: + exclude: + - packages/contracts-bedrock/ From 11f0298e1ebd25a0783d7cb798358dea04ecc235 Mon Sep 17 00:00:00 2001 From: Inphi Date: Mon, 4 Nov 2024 07:30:15 -0800 Subject: [PATCH 119/451] cannon: Add godoc to ExecMipsCoreStepLogic (#12786) * cannon: Add godoc to ExecMipsCoreStepLogic * update MIPS64.sol * update semver --- cannon/mipsevm/exec/mips_instructions.go | 6 +++-- cannon/mipsevm/multithreaded/mips.go | 8 +++---- packages/contracts-bedrock/semver-lock.json | 4 ++-- .../contracts-bedrock/src/cannon/MIPS64.sol | 14 ++++++------ .../cannon/libraries/MIPS64Instructions.sol | 22 +++++++++---------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index b72c5113e607..bf1a8fdd0e7c 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -33,7 +33,9 @@ func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun ui return insn, opcode, fun } -func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) (memUpdated bool, memAddr Word, err error) { +// ExecMipsCoreStepLogic executes a MIPS instruction that isn't a syscall nor a RMW operation +// If a store operation occurred, then it returns the effective address of the store memory location. +func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) (memUpdated bool, effMemAddr Word, err error) { // j-type j/jal if opcode == 2 || opcode == 3 { linkReg := Word(0) @@ -153,7 +155,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory memTracker.TrackMemAccess(storeAddr) memory.SetWord(storeAddr, val) memUpdated = true - memAddr = storeAddr + effMemAddr = storeAddr } // write back the value to destination register diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index 65c911d9c9eb..4051465a3023 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -316,19 +316,19 @@ func (m *InstrumentedState) mipsStep() error { } // Exec the rest of the step logic - memUpdated, memAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) + memUpdated, effMemAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) if err != nil { return err } if memUpdated { - m.handleMemoryUpdate(memAddr) + m.handleMemoryUpdate(effMemAddr) } return nil } -func (m *InstrumentedState) handleMemoryUpdate(memAddr Word) { - if memAddr == (arch.AddressMask & m.state.LLAddress) { +func (m *InstrumentedState) handleMemoryUpdate(effMemAddr Word) { + if effMemAddr == (arch.AddressMask & m.state.LLAddress) { // Reserved address was modified, clear the reservation m.clearLLMemoryReservation() } diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 1cee134ca198..62672e3852f0 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -148,8 +148,8 @@ "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" }, "src/cannon/MIPS64.sol": { - "initCodeHash": "0x5094e3f488de838cba1422b508a899444ee27905abc9c0b97961d4b8b7392289", - "sourceCodeHash": "0xdc2393d2073348218a9c6e169c2e7f687590e237773b8fde53fae9cf84395978" + "initCodeHash": "0xc615a8f321f3bfb29c9459705f3012e512c463ba9ba2e1bcb6f9349f2d911d10", + "sourceCodeHash": "0x8fd0f4167d80f48010ad1241a9f6fb93576887dcc1f97be6b05adaf0086a537b" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS64.sol b/packages/contracts-bedrock/src/cannon/MIPS64.sol index 8c85a26acd43..3ca24d632eed 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS64.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS64.sol @@ -64,8 +64,8 @@ contract MIPS64 is ISemver { } /// @notice The semantic version of the MIPS64 contract. - /// @custom:semver 1.0.0-beta.2 - string public constant version = "1.0.0-beta.2"; + /// @custom:semver 1.0.0-beta.3 + string public constant version = "1.0.0-beta.3"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -272,20 +272,20 @@ contract MIPS64 is ISemver { fun: fun }); bool memUpdated; - uint64 memAddr; - (state.memRoot, memUpdated, memAddr) = ins.execMipsCoreStepLogic(coreStepArgs); + uint64 effMemAddr; + (state.memRoot, memUpdated, effMemAddr) = ins.execMipsCoreStepLogic(coreStepArgs); setStateCpuScalars(thread, cpu); updateCurrentThreadRoot(); if (memUpdated) { - handleMemoryUpdate(state, memAddr); + handleMemoryUpdate(state, effMemAddr); } return outputState(); } } - function handleMemoryUpdate(State memory _state, uint64 _memAddr) internal pure { - if (_memAddr == (arch.ADDRESS_MASK & _state.llAddress)) { + function handleMemoryUpdate(State memory _state, uint64 _effMemAddr) internal pure { + if (_effMemAddr == (arch.ADDRESS_MASK & _state.llAddress)) { // Reserved address was modified, clear the reservation clearLLMemoryReservation(_state); } diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol index 98bf87ec6239..d24ec036e819 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol @@ -66,23 +66,23 @@ library MIPS64Instructions { /// @notice Execute core MIPS step logic. /// @return newMemRoot_ The updated merkle root of memory after any modifications, may be unchanged. /// @return memUpdated_ True if memory was modified. - /// @return memAddr_ Holds the memory address that was updated if memUpdated_ is true. + /// @return effMemAddr_ Holds the effective address that was updated if memUpdated_ is true. function execMipsCoreStepLogic(CoreStepLogicParams memory _args) internal pure - returns (bytes32 newMemRoot_, bool memUpdated_, uint64 memAddr_) + returns (bytes32 newMemRoot_, bool memUpdated_, uint64 effMemAddr_) { unchecked { newMemRoot_ = _args.memRoot; memUpdated_ = false; - memAddr_ = 0; + effMemAddr_ = 0; // j-type j/jal if (_args.opcode == 2 || _args.opcode == 3) { // Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset uint64 target = (_args.cpu.nextPC & signExtend(0xF0000000, 32)) | uint64((_args.insn & 0x03FFFFFF) << 2); handleJump(_args.cpu, _args.registers, _args.opcode == 2 ? 0 : REG_RA, target); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } // register fetch @@ -129,7 +129,7 @@ library MIPS64Instructions { _rtReg: rtReg, _rs: rs }); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } uint64 storeAddr = U64_MASK; @@ -162,18 +162,18 @@ library MIPS64Instructions { if (_args.fun == 8 || _args.fun == 9) { // jr/jalr handleJump(_args.cpu, _args.registers, _args.fun == 8 ? 0 : rdReg, rs); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } if (_args.fun == 0xa) { // movz handleRd(_args.cpu, _args.registers, rdReg, rs, rt == 0); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } if (_args.fun == 0xb) { // movn handleRd(_args.cpu, _args.registers, rdReg, rs, rt != 0); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } // lo and hi registers @@ -187,7 +187,7 @@ library MIPS64Instructions { _rt: rt, _storeReg: rdReg }); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } } @@ -195,13 +195,13 @@ library MIPS64Instructions { if (storeAddr != U64_MASK) { newMemRoot_ = MIPS64Memory.writeMem(storeAddr, _args.memProofOffset, val); memUpdated_ = true; - memAddr_ = storeAddr; + effMemAddr_ = storeAddr; } // write back the value to destination register handleRd(_args.cpu, _args.registers, rdReg, val, true); - return (newMemRoot_, memUpdated_, memAddr_); + return (newMemRoot_, memUpdated_, effMemAddr_); } } From 0eb090e15849e74836e050dc414647d54e5bb250 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Mon, 4 Nov 2024 14:24:58 -0500 Subject: [PATCH 120/451] fix: Security reviews clean (#12811) This updates the markdown table in security-reviews/README.md to: 1. Correct the scope of the 2024-03 Sherlock audit to Fault Proofs. 2. Rename pdfs to match the existing convetion 3. Fix Reviewer attribution on the Mips and no Mips audits. --- ...ps.pdf => 2024_08_Fault-Proofs-MIPS_Cantina.pdf} | Bin ...df => 2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf} | Bin docs/security-reviews/README.md | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) rename docs/security-reviews/{2024_08_report-cantinacode-coinbase-fault-proofs-mips.pdf => 2024_08_Fault-Proofs-MIPS_Cantina.pdf} (100%) rename docs/security-reviews/{2024_08_report-cb-fault-proofs-non-mips.pdf => 2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf} (100%) diff --git a/docs/security-reviews/2024_08_report-cantinacode-coinbase-fault-proofs-mips.pdf b/docs/security-reviews/2024_08_Fault-Proofs-MIPS_Cantina.pdf similarity index 100% rename from docs/security-reviews/2024_08_report-cantinacode-coinbase-fault-proofs-mips.pdf rename to docs/security-reviews/2024_08_Fault-Proofs-MIPS_Cantina.pdf diff --git a/docs/security-reviews/2024_08_report-cb-fault-proofs-non-mips.pdf b/docs/security-reviews/2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf similarity index 100% rename from docs/security-reviews/2024_08_report-cb-fault-proofs-non-mips.pdf rename to docs/security-reviews/2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf diff --git a/docs/security-reviews/README.md b/docs/security-reviews/README.md index 79e756e43f57..483dc1541858 100644 --- a/docs/security-reviews/README.md +++ b/docs/security-reviews/README.md @@ -24,8 +24,8 @@ Please see the report for the specific details. | 2023-12 | Trust | Superchain Config Upgrade: `SuperchainConfig.sol`, `L1CrossDomainMessenger.sol`, `L1ERC721Bridge.sol`, `L1StandardBridge.sol`, `OptimismPortal.sol`, `CrossDomainMessenger.sol`, `ERC721Bridge.sol`, `StandardBridge.sol` | [2023_12_SuperchainConfigUpgrade_Trust.pdf](./2023_12_SuperchainConfigUpgrade_Trust.pdf) | d1651bb22645ebd41ac4bb2ab4786f9a56fc1003 | op-contracts/v1.2.0 | | 2024-02 | Runtime Verification | Pausability | [Kontrol Verification][kontrol] | | | | 2024-02 | Cantina | MCP L1: `OptimismPortal.sol`, `L1CrossDomainMessenger.sol`, `L1StandardBridge.sol`, `L1ERC721Bridge.sol`, `OptimismMintableERC20Factory.sol`, `L2OutputOracle.sol`, `SystemConfig.sol` | [2024_02-MCP_L1-Cantina.pdf](./2024_02-MCP_L1-Cantina.pdf) | e6ef3a900c42c8722e72c2e2314027f85d12ced5 | op-contracts/v1.3.0 | -| 2024-03 | Sherlock | MCP L1 | Sherlock Optimism Fault Proofs Contest ([site](https://audits.sherlock.xyz/contests/205), [repo](https://github.com/sherlock-audit/2024-02-optimism-2024)) | | | -| 2024-08 | Spearbit | Fault proof no-MIPS: All contracts in the `packages/contracts-bedrock/src/dispute` directory | [Base Fault Proof No MIPS](./2024_08_report-cb-fault-proofs-non-mips.pdf) | 1f7081798ce2d49b8643514663d10681cb853a3d | op-contracts/v1.4.0 | -| 2024-08 | Cantina | Fault proof MIPS: `MIPS.sol` | [Base Fault Proof MIPS](./2024_08_report-cantinacode-coinbase-fault-proofs-mips.pdf) | 71b93116738ee98c9f8713b1a5dfe626ce06c1b2 | op-contracts/v1.6.0 | +| 2024-03 | Sherlock | Fault Proofs | Sherlock Optimism Fault Proofs Contest ([site](https://audits.sherlock.xyz/contests/205), [repo](https://github.com/sherlock-audit/2024-02-optimism-2024)) | | | +| 2024-08 | Cantina | Fault proof no-MIPS: All contracts in the `packages/contracts-bedrock/src/dispute` directory | [./2024_08_Fault-Proofs-MIPS_Cantina.pdf](./2024_08_Fault-Proofs-MIPS_Cantina.pdf) | 1f7081798ce2d49b8643514663d10681cb853a3d | op-contracts/v1.4.0 | +| 2024-08 | Spearbit | Fault proof MIPS: `MIPS.sol` | [./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf](./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf) | 71b93116738ee98c9f8713b1a5dfe626ce06c1b2 | op-contracts/v1.6.0 | [kontrol]: https://github.com/ethereum-optimism/optimism/blob/876e16ad04968f0bb641eb76f98eb77e7e1a3e16/packages/contracts-bedrock/test/kontrol/README.md From f80eb8dd2d4371ba0e303e15d364b9171c0cf08d Mon Sep 17 00:00:00 2001 From: mbaxter Date: Mon, 4 Nov 2024 14:37:11 -0500 Subject: [PATCH 121/451] cannon: Add post-state checks (#12809) * cannon: Add post-state assertions * cannon: Bump MIPS64 contract version --- cannon/mipsevm/multithreaded/mips.go | 17 +++++++++ packages/contracts-bedrock/semver-lock.json | 4 +- .../snapshots/abi/MIPS64.json | 2 +- .../contracts-bedrock/src/cannon/MIPS64.sol | 38 +++++++++++++++++-- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index 4051465a3023..b537953a31e6 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -224,6 +224,23 @@ func (m *InstrumentedState) handleSyscall() error { } func (m *InstrumentedState) mipsStep() error { + err := m.doMipsStep() + if err != nil { + return err + } + + m.assertPostStateChecks() + return err +} + +func (m *InstrumentedState) assertPostStateChecks() { + activeStack := m.state.getActiveThreadStack() + if len(activeStack) == 0 { + panic("post-state active thread stack is empty") + } +} + +func (m *InstrumentedState) doMipsStep() error { if m.state.Exited { return nil } diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 62672e3852f0..8082c799f755 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -148,8 +148,8 @@ "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" }, "src/cannon/MIPS64.sol": { - "initCodeHash": "0xc615a8f321f3bfb29c9459705f3012e512c463ba9ba2e1bcb6f9349f2d911d10", - "sourceCodeHash": "0x8fd0f4167d80f48010ad1241a9f6fb93576887dcc1f97be6b05adaf0086a537b" + "initCodeHash": "0x93aa8d7f9fd3c22276c0d303a3fefdf8f73cc55807b35e483bba64c92d02aaef", + "sourceCodeHash": "0x171d66c651fdad2ac9c287da92689815a5b09589945ada092179508ad2326306" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/snapshots/abi/MIPS64.json b/packages/contracts-bedrock/snapshots/abi/MIPS64.json index fe6007b60a89..a12c96c7a70d 100644 --- a/packages/contracts-bedrock/snapshots/abi/MIPS64.json +++ b/packages/contracts-bedrock/snapshots/abi/MIPS64.json @@ -45,7 +45,7 @@ "outputs": [ { "internalType": "bytes32", - "name": "", + "name": "postState_", "type": "bytes32" } ], diff --git a/packages/contracts-bedrock/src/cannon/MIPS64.sol b/packages/contracts-bedrock/src/cannon/MIPS64.sol index 3ca24d632eed..53dd0649405d 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS64.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS64.sol @@ -64,8 +64,8 @@ contract MIPS64 is ISemver { } /// @notice The semantic version of the MIPS64 contract. - /// @custom:semver 1.0.0-beta.3 - string public constant version = "1.0.0-beta.3"; + /// @custom:semver 1.0.0-beta.4 + string public constant version = "1.0.0-beta.4"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -106,7 +106,39 @@ contract MIPS64 is ISemver { /// the current thread stack. /// @param _localContext The local key context for the preimage oracle. Optional, can be set as a constant /// if the caller only requires one set of local keys. - function step(bytes calldata _stateData, bytes calldata _proof, bytes32 _localContext) public returns (bytes32) { + /// @return postState_ The hash of the post state witness after the state transition. + function step( + bytes calldata _stateData, + bytes calldata _proof, + bytes32 _localContext + ) + public + returns (bytes32 postState_) + { + postState_ = doStep(_stateData, _proof, _localContext); + assertPostStateChecks(); + } + + function assertPostStateChecks() internal pure { + State memory state; + assembly { + state := STATE_MEM_OFFSET + } + + bytes32 activeStack = state.traverseRight ? state.rightThreadStack : state.leftThreadStack; + if (activeStack == EMPTY_THREAD_ROOT) { + revert("MIPS64: post-state active thread stack is empty"); + } + } + + function doStep( + bytes calldata _stateData, + bytes calldata _proof, + bytes32 _localContext + ) + internal + returns (bytes32) + { unchecked { State memory state; ThreadState memory thread; From 9bdcb02631d47994aa49f4f23595376d1e816dcf Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 4 Nov 2024 13:20:06 -0700 Subject: [PATCH 122/451] op-deployer: Fix useInterop flag (#12813) * op-deployer: Fix useInterop flag * set interop offset --- op-deployer/pkg/deployer/state/deploy_config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 534b1292d125..15d5491ce8b8 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -70,7 +70,7 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, L2GenesisEcotoneTimeOffset: u64UtilPtr(0), L2GenesisFjordTimeOffset: u64UtilPtr(0), L2GenesisGraniteTimeOffset: u64UtilPtr(0), - UseInterop: false, + UseInterop: intent.UseInterop, }, L2CoreDeployConfig: genesis.L2CoreDeployConfig{ L1ChainID: intent.L1ChainID, @@ -102,6 +102,10 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, }, } + if intent.UseInterop { + cfg.L2InitializationConfig.UpgradeScheduleDeployConfig.L2GenesisInteropTimeOffset = u64UtilPtr(0) + } + if chainState.StartBlock == nil { // These are dummy variables - see below for rationale. num := rpc.LatestBlockNumber From 94ab846ab439ef26802ca32d6bc5921aadbafa0f Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:04:47 -0300 Subject: [PATCH 123/451] feat: implement IERC7802 (#12790) * feat: add IERC7802 (#123) * feat: add IERC7802 * fix: token address fuzz error * feat: remove ERC165 contract inheritance * feat: add IERC20 interface support (#124) * feat: add IERC20 interface support * fix: interfaces tests --- packages/contracts-bedrock/semver-lock.json | 14 ++++----- .../snapshots/abi/SuperchainTokenBridge.json | 5 ++++ .../snapshots/abi/SuperchainWETH.json | 19 ++++++++++++ .../src/L2/OptimismSuperchainERC20.sol | 7 ++--- .../src/L2/SuperchainERC20.sol | 20 ++++++++----- .../src/L2/SuperchainTokenBridge.sol | 11 +++++-- .../src/L2/SuperchainWETH.sol | 15 +++++++--- .../{ICrosschainERC20.sol => IERC7802.sol} | 6 ++-- .../src/L2/interfaces/ISuperchainERC20.sol | 6 ++-- .../L2/interfaces/ISuperchainTokenBridge.sol | 1 + .../src/L2/interfaces/ISuperchainWETH.sol | 5 ++-- .../test/L2/SuperchainERC20.t.sol | 24 ++++++++++++--- .../test/L2/SuperchainTokenBridge.t.sol | 30 +++++++++++++++++-- .../test/L2/SuperchainWETH.t.sol | 18 +++++++++++ 14 files changed, 145 insertions(+), 36 deletions(-) rename packages/contracts-bedrock/src/L2/interfaces/{ICrosschainERC20.sol => IERC7802.sol} (89%) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 8082c799f755..1e14f5d286b5 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -108,8 +108,8 @@ "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x24d85d246858d1aff78ae86c614dd0dc0f63b3326b2b662e3462c3a6f9b7965e", - "sourceCodeHash": "0xcb705d26e63e733051c8bd442ea69ce637a00c16d646ccc37b687b20941366fe" + "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9", + "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", @@ -125,15 +125,15 @@ }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "sourceCodeHash": "0xba47f404e66e010ce417410476f26c704f2be4ce584cb79210bc5536a82ddb1f" + "sourceCodeHash": "0xcf39c16893cace1e7d61350bfff05a27f3ce8da8eb0ac02cb5ac7bf603f163fa" }, "src/L2/SuperchainTokenBridge.sol": { - "initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", - "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" + "initCodeHash": "0x1cd2afdae6dd1b6ebc17f1d529e7d74c9b8b21b02db8589b8e389e2d5523d775", + "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0xc72cb486b815a65daa8bd5d0af8c965b6708cf8caf03de0a18023a63a6e6c604", - "sourceCodeHash": "0x39fff1d4702a2fec3dcba29c7f9604eabf20d32e9c5bf4377edeb620624aa467" + "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97", + "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359" }, "src/L2/WETH.sol": { "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json b/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json index 36358db1b307..a75bb2ba7234 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json @@ -153,6 +153,11 @@ "name": "InvalidCrossDomainSender", "type": "error" }, + { + "inputs": [], + "name": "InvalidERC7802", + "type": "error" + }, { "inputs": [], "name": "Unauthorized", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json index fe53a4cbfb5e..32d84df7d889 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json @@ -143,6 +143,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "symbol", diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index 1e7cdb4a7190..c323d8b7577b 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.25; import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { ERC165 } from "@openzeppelin/contracts-v5/utils/introspection/ERC165.sol"; import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol"; @@ -16,7 +15,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol /// OptimismSuperchainERC20 token, turning it fungible and interoperable across the superchain. Likewise, it /// also enables the inverse conversion path. /// Moreover, it builds on top of the L2ToL2CrossDomainMessenger for both replay protection and domain binding. -contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { +contract OptimismSuperchainERC20 is SuperchainERC20, Initializable { /// @notice Emitted whenever tokens are minted for an account. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. @@ -59,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.8 - string public constant override version = "1.0.0-beta.8"; + /// @custom:semver 1.0.0-beta.9 + string public constant override version = "1.0.0-beta.9"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol index f18f91ad2413..b9e6bbfbf780 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { ERC20 } from "@solady-v0.0.245/tokens/ERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; /// @title SuperchainERC20 -/// @notice SuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token -/// bridging to make it fungible across the Superchain. This construction allows the SuperchainTokenBridge to -/// burn and mint tokens. -abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { +/// @notice A standard ERC20 extension implementing IERC7802 for unified cross-chain fungibility across +/// the Superchain. Allows the SuperchainTokenBridge to mint and burn tokens as needed. +abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.4 + /// @custom:semver 1.0.0-beta.5 function version() external view virtual returns (string memory) { - return "1.0.0-beta.4"; + return "1.0.0-beta.5"; } /// @notice Allows the SuperchainTokenBridge to mint tokens. @@ -39,4 +39,10 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { emit CrosschainBurn(_from, _amount); } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) { + return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId + || _interfaceId == type(IERC165).interfaceId; + } } diff --git a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol index 284beb79ec0e..104f8ef95043 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol @@ -7,6 +7,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol // Interfaces import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; /// @custom:proxied true @@ -20,6 +21,9 @@ contract SuperchainTokenBridge { /// SuperchainTokenBridge. error InvalidCrossDomainSender(); + /// @notice Thrown when attempting to use a token that does not implement the ERC7802 interface. + error InvalidERC7802(); + /// @notice Emitted when tokens are sent from one chain to another. /// @param token Address of the token sent. /// @param from Address of the sender. @@ -42,8 +46,8 @@ contract SuperchainTokenBridge { address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.2 - string public constant version = "1.0.0-beta.2"; + /// @custom:semver 1.0.0-beta.3 + string public constant version = "1.0.0-beta.3"; /// @notice Sends tokens to a target address on another chain. /// @dev Tokens are burned on the source chain. @@ -63,6 +67,8 @@ contract SuperchainTokenBridge { { if (_to == address(0)) revert ZeroAddress(); + if (!IERC165(_token).supportsInterface(type(IERC7802).interfaceId)) revert InvalidERC7802(); + ISuperchainERC20(_token).crosschainBurn(msg.sender, _amount); bytes memory message = abi.encodeCall(this.relayERC20, (_token, msg.sender, _to, _amount)); @@ -82,6 +88,7 @@ contract SuperchainTokenBridge { (address crossDomainMessageSender, uint256 source) = IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); + if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); ISuperchainERC20(_token).crosschainMint(_to, _amount); diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index e03b689ba150..29e179eba82c 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -12,7 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; -import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; /// @custom:proxied true @@ -21,10 +22,10 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// @notice SuperchainWETH is a version of WETH that can be freely transfrered between chains /// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// do not use a custom gas token. -contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { +contract SuperchainWETH is WETH98, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @inheritdoc WETH98 function deposit() public payable override { @@ -91,4 +92,10 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { emit CrosschainBurn(_from, _amount); } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) { + return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId + || _interfaceId == type(IERC165).interfaceId; + } } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol similarity index 89% rename from packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol rename to packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol index 9fbfe8c65f33..469230e822d1 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol @@ -1,9 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -/// @title ICrosschainERC20 +import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; + +/// @title IERC7802 /// @notice Defines the interface for crosschain ERC20 transfers. -interface ICrosschainERC20 { +interface IERC7802 is IERC165 { /// @notice Emitted when a crosschain transfer mints tokens. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol index ce066a27b88c..029b13d5520a 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol @@ -2,15 +2,17 @@ pragma solidity ^0.8.0; // Interfaces -import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; +import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title ISuperchainERC20 /// @notice This interface is available on the SuperchainERC20 contract. /// @dev This interface is needed for the abstract SuperchainERC20 implementation but is not part of the standard -interface ISuperchainERC20 is ICrosschainERC20, IERC20, ISemver { +interface ISuperchainERC20 is IERC7802, IERC20, ISemver { error Unauthorized(); + function supportsInterface(bytes4 _interfaceId) external view returns (bool); + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol index f2a61d02d555..af9d7d8d8411 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol @@ -9,6 +9,7 @@ interface ISuperchainTokenBridge is ISemver { error ZeroAddress(); error Unauthorized(); error InvalidCrossDomainSender(); + error InvalidERC7802(); event SendERC20( address indexed token, address indexed from, address indexed to, uint256 amount, uint256 destination diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol index 1935b4d5b0b5..fa10b237dbb5 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol @@ -2,15 +2,16 @@ pragma solidity ^0.8.0; import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; -import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; +import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -interface ISuperchainWETH is IWETH98, ICrosschainERC20, ISemver { +interface ISuperchainWETH is IWETH98, IERC7802, ISemver { error Unauthorized(); error NotCustomGasToken(); function balanceOf(address src) external view returns (uint256); function withdraw(uint256 _amount) external; + function supportsInterface(bytes4 _interfaceId) external view returns (bool); function __constructor__() external; } diff --git a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol index f814c06c76df..a9aa3b501618 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol @@ -6,11 +6,11 @@ import { Test } from "forge-std/Test.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; -import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol"; // Target contract import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; -import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { MockSuperchainERC20Implementation } from "test/mocks/SuperchainERC20Implementation.sol"; @@ -62,7 +62,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainMint(_to, _amount); + emit IERC7802.CrosschainMint(_to, _amount); // Call the `mint` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -105,7 +105,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainBurn(_from, _amount); + emit IERC7802.CrosschainBurn(_from, _amount); // Call the `burn` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -115,4 +115,20 @@ contract SuperchainERC20Test is Test { assertEq(superchainERC20.totalSupply(), _totalSupplyBefore - _amount); assertEq(superchainERC20.balanceOf(_from), _fromBalanceBefore - _amount); } + + /// @notice Tests that the `supportsInterface` function returns true for the `IERC7802` interface. + function test_supportInterface_succeeds() public view { + assertTrue(superchainERC20.supportsInterface(type(IERC165).interfaceId)); + assertTrue(superchainERC20.supportsInterface(type(IERC7802).interfaceId)); + assertTrue(superchainERC20.supportsInterface(type(IERC20).interfaceId)); + } + + /// @notice Tests that the `supportsInterface` function returns false for any other interface than the + /// `IERC7802` one. + function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { + vm.assume(_interfaceId != type(IERC165).interfaceId); + vm.assume(_interfaceId != type(IERC7802).interfaceId); + vm.assume(_interfaceId != type(IERC20).interfaceId); + assertFalse(superchainERC20.supportsInterface(_interfaceId)); + } } diff --git a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol index 2ff6cecfd12b..1dc01ba0008e 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol @@ -13,6 +13,7 @@ import { ISuperchainTokenBridge } from "src/L2/interfaces/ISuperchainTokenBridge import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol"; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; +import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; /// @title SuperchainTokenBridgeTest /// @notice Contract for testing the SuperchainTokenBridge contract. @@ -60,6 +61,32 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { superchainTokenBridge.sendERC20(address(superchainERC20), ZERO_ADDRESS, _amount, _chainId); } + /// @notice Tests the `sendERC20` function reverts when the `token` does not support the IERC7802 interface. + function testFuzz_sendERC20_notSupportedIERC7802_reverts( + address _token, + address _sender, + address _to, + uint256 _amount, + uint256 _chainId + ) + public + { + vm.assume(_to != ZERO_ADDRESS); + assumeAddressIsNot(_token, AddressType.Precompile, AddressType.ForgeAddress); + + // Mock the call over the `supportsInterface` function to return false + vm.mockCall( + _token, abi.encodeCall(ISuperchainERC20.supportsInterface, (type(IERC7802).interfaceId)), abi.encode(false) + ); + + // Expect the revert with `InvalidERC7802` selector + vm.expectRevert(ISuperchainTokenBridge.InvalidERC7802.selector); + + // Call the `sendERC20` function + vm.prank(_sender); + superchainTokenBridge.sendERC20(_token, _to, _amount, _chainId); + } + /// @notice Tests the `sendERC20` function burns the sender tokens, sends the message, and emits the `SendERC20` /// event. function testFuzz_sendERC20_succeeds( @@ -137,7 +164,6 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { /// @notice Tests the `relayERC20` function reverts when the `crossDomainMessageSender` that sent the message is not /// the same SuperchainTokenBridge. function testFuzz_relayERC20_notCrossDomainSender_reverts( - address _token, address _crossDomainMessageSender, uint256 _source, address _to, @@ -159,7 +185,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { // Call the `relayERC20` function with the sender caller vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - superchainTokenBridge.relayERC20(_token, _crossDomainMessageSender, _to, _amount); + superchainTokenBridge.relayERC20(address(superchainERC20), _crossDomainMessageSender, _to, _amount); } /// @notice Tests the `relayERC20` mints the proper amount and emits the `RelayERC20` event. diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index 9a65544dc7ee..4d4109b6885a 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -12,6 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Interfaces import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title SuperchainWETH_Test /// @notice Contract for testing the SuperchainWETH contract. @@ -457,4 +459,20 @@ contract SuperchainWETH_Test is CommonTest { // Assert assertEq(superchainWeth.balanceOf(_user), _wad); } + + /// @notice Tests that the `supportsInterface` function returns true for the `IERC7802` interface. + function test_supportInterface_succeeds() public view { + assertTrue(superchainWeth.supportsInterface(type(IERC165).interfaceId)); + assertTrue(superchainWeth.supportsInterface(type(IERC7802).interfaceId)); + assertTrue(superchainWeth.supportsInterface(type(IERC20).interfaceId)); + } + + /// @notice Tests that the `supportsInterface` function returns false for any other interface than the + /// `IERC7802` one. + function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { + vm.assume(_interfaceId != type(IERC165).interfaceId); + vm.assume(_interfaceId != type(IERC7802).interfaceId); + vm.assume(_interfaceId != type(IERC20).interfaceId); + assertFalse(superchainWeth.supportsInterface(_interfaceId)); + } } From c1c0d2c5177bc4e24a2e4e1441813a3f611c3a7a Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 4 Nov 2024 19:52:37 -0500 Subject: [PATCH 124/451] feat: Update `kona` in `proofs-tools` image (#12817) --- docker-bake.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-bake.hcl b/docker-bake.hcl index 4f72678a8cab..5f7f311cefe4 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -207,7 +207,7 @@ target "proofs-tools" { context = "." args = { CHALLENGER_VERSION="b46bffed42db3442d7484f089278d59f51503049" - KONA_VERSION="kona-client-v0.1.0-alpha.6" + KONA_VERSION="kona-client-v0.1.0-alpha.7" } target="proofs-tools" platforms = split(",", PLATFORMS) From b638304da21b2ae8b5208a70c49f4d463e6b2e6b Mon Sep 17 00:00:00 2001 From: ControlCplusControlV <44706811+ControlCplusControlV@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:03:53 -0500 Subject: [PATCH 125/451] Move top-level markdown files into meta/docs folder (#12727) * Moved meta files to a new folder * doctoc POLICY * fix versioning * remove doctoc on versioning --- packages/contracts-bedrock/README.md | 8 ++--- .../{ => meta}/CONTRIBUTING.md | 0 .../{ => meta}/INTERFACES.md | 0 packages/contracts-bedrock/{ => meta}/LICENSE | 0 packages/contracts-bedrock/meta/POLICY.md | 36 +++++++++++++++++++ .../{ => meta}/SOLIDITY_UPGRADES.md | 0 .../{ => meta}/STYLE_GUIDE.md | 0 .../{ => meta}/VERSIONING.md | 12 ------- 8 files changed, 40 insertions(+), 16 deletions(-) rename packages/contracts-bedrock/{ => meta}/CONTRIBUTING.md (100%) rename packages/contracts-bedrock/{ => meta}/INTERFACES.md (100%) rename packages/contracts-bedrock/{ => meta}/LICENSE (100%) create mode 100644 packages/contracts-bedrock/meta/POLICY.md rename packages/contracts-bedrock/{ => meta}/SOLIDITY_UPGRADES.md (100%) rename packages/contracts-bedrock/{ => meta}/STYLE_GUIDE.md (100%) rename packages/contracts-bedrock/{ => meta}/VERSIONING.md (91%) diff --git a/packages/contracts-bedrock/README.md b/packages/contracts-bedrock/README.md index f3c70baf0914..a9f1dbeeaa5f 100644 --- a/packages/contracts-bedrock/README.md +++ b/packages/contracts-bedrock/README.md @@ -66,22 +66,22 @@ See the [Optimism Developer Docs](https://docs.optimism.io/chain/addresses) for ### Contributing Guide Contributions to the OP Stack are always welcome. -Please refer to the [CONTRIBUTING.md](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/CONTRIBUTING.md) for more information about how to contribute to the OP Stack smart contracts. +Please refer to the [CONTRIBUTING.md](./meta/CONTRIBUTING.md) for more information about how to contribute to the OP Stack smart contracts. ### Style Guide -OP Stack smart contracts should be written according to the [STYLE_GUIDE.md](./STYLE_GUIDE.md) found within this repository. +OP Stack smart contracts should be written according to the [STYLE_GUIDE.md](./meta/STYLE_GUIDE.md) found within this repository. Maintaining a consistent code style makes code easier to review and maintain, ultimately making the development process safer. ### Contract Interfaces OP Stack smart contracts use contract interfaces in a relatively unique way. Please refer to -[INTERFACES.md](./INTERFACES.md) to read more about how the OP Stack uses contract interfaces. +[INTERFACES.md](./meta/INTERFACES.md) to read more about how the OP Stack uses contract interfaces. ### Solidity Versioning OP Stack smart contracts are designed to utilize a single, consistent Solidity version. Please -refer to [SOLIDITY_UPGRADES.md](./SOLIDITY_UPGRADES.md) to understand the process for updating to +refer to [SOLIDITY_UPGRADES.md](./meta/SOLIDITY_UPGRADES.md) to understand the process for updating to newer Solidity versions. ## Deployment diff --git a/packages/contracts-bedrock/CONTRIBUTING.md b/packages/contracts-bedrock/meta/CONTRIBUTING.md similarity index 100% rename from packages/contracts-bedrock/CONTRIBUTING.md rename to packages/contracts-bedrock/meta/CONTRIBUTING.md diff --git a/packages/contracts-bedrock/INTERFACES.md b/packages/contracts-bedrock/meta/INTERFACES.md similarity index 100% rename from packages/contracts-bedrock/INTERFACES.md rename to packages/contracts-bedrock/meta/INTERFACES.md diff --git a/packages/contracts-bedrock/LICENSE b/packages/contracts-bedrock/meta/LICENSE similarity index 100% rename from packages/contracts-bedrock/LICENSE rename to packages/contracts-bedrock/meta/LICENSE diff --git a/packages/contracts-bedrock/meta/POLICY.md b/packages/contracts-bedrock/meta/POLICY.md new file mode 100644 index 000000000000..e2759f4eff12 --- /dev/null +++ b/packages/contracts-bedrock/meta/POLICY.md @@ -0,0 +1,36 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Policy](#policy) + - [Contributing](#contributing) + - [Versioning Policy](#versioning-policy) + - [Upgrade Policy](#upgrade-policy) + - [Style Guide](#style-guide) + - [Revert Data](#revert-data) + + + +# Policy + +This document outlines upgrade policies regarding the OP Stack codebase. + +## Contributing + +For any policies on contributing, please see [CONTRIBUTING](./CONTRIBUTING.md) + +## Versioning Policy + +For our versioning policy, please see our policy on [VERSIONING](./VERSIONING.md) + +## Upgrade Policy + +For the solidity upgrade policy, please see our doc on [SOLIDITY UPGRADES](./SOLIDITY_UPGRADES.md) + +## Style Guide + +For an indepth review of the code style used in the OP Stack contracts, please see our [STYLE GUIDE](./STYLE_GUIDE.md) + +## Revert Data + +Revert data may be changed in the future, and is not a reliable interface for external consumers. Contracts should not depend on specific revert data returned by OP Stack contracts, which can be changed during any future OP Stack contract upgrades. Revert data includes both custom errors returned by contracts, as a well as revert strings. diff --git a/packages/contracts-bedrock/SOLIDITY_UPGRADES.md b/packages/contracts-bedrock/meta/SOLIDITY_UPGRADES.md similarity index 100% rename from packages/contracts-bedrock/SOLIDITY_UPGRADES.md rename to packages/contracts-bedrock/meta/SOLIDITY_UPGRADES.md diff --git a/packages/contracts-bedrock/STYLE_GUIDE.md b/packages/contracts-bedrock/meta/STYLE_GUIDE.md similarity index 100% rename from packages/contracts-bedrock/STYLE_GUIDE.md rename to packages/contracts-bedrock/meta/STYLE_GUIDE.md diff --git a/packages/contracts-bedrock/VERSIONING.md b/packages/contracts-bedrock/meta/VERSIONING.md similarity index 91% rename from packages/contracts-bedrock/VERSIONING.md rename to packages/contracts-bedrock/meta/VERSIONING.md index fefc2211b3e5..cb2b6b692124 100644 --- a/packages/contracts-bedrock/VERSIONING.md +++ b/packages/contracts-bedrock/meta/VERSIONING.md @@ -11,7 +11,6 @@ There are five parts to the versioning and release process: - [Release Process](#release-process): The process for deploying contracts, creating a governance proposal, and the required associated releases. - [Additional Release Candidates](#additional-release-candidates): How to handle additional release candidates after an initial `op-contracts/vX.Y.Z-rc.1` release. - [Merging Back to Develop After Governance Approval](#merging-back-to-develop-after-governance-approval): Explains how to choose the resulting contract versions when merging back into `develop`. -- [Changelog](#changelog): A CHANGELOG for contract releases is maintained. > [!NOTE] > The rules described in this document must be enforced manually. @@ -119,14 +118,3 @@ Now there are two scenarios for the PR that merges the release branch back into - In practice, this one unlikely to occur when using inheritance for feature development, as specified in [Smart Contract Feature Development](https://github.com/ethereum-optimism/design-docs/blob/main/smart-contract-feature-development.md) architecture. It's more likely that (1) is the case, and we merge the version change into the base contract. This flow also provides a dedicated branch for each release, making it easy to deploy a patch or bug fix, regardless of other changes that may have occurred on develop since the release. - -## Changelog - -Lastly, a CHANGELOG for contract releases must be maintained: - -- Each upcoming release will have a tracking issue that documents the new versions of each contract that will be included in the release, along with links to the PRs that made the changes. -- Every contracts PR must have an accompanying changelog entry in a tracking issue once it is merged. -- Tracking issue titles should be named based on the expected Upgrade number they will go to governance with, e.g. "op-contracts changelog: Upgrade 9". - - See [ethereum-optimism/optimism#10592](https://github.com/ethereum-optimism/optimism/issues/10592) for an example of what this tracking issue should look like. - - We do not include a version number in the issue because it may be hard to predict the final version number of a release until all PRs are merged. - - Using upgrade numbers also acts as a forcing function to ensure upgrade sequencing and the governance process is accounted for early in the development process. From 5cb2daba68f5d3b6f4fd51c33d12f8861a4b57fc Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 5 Nov 2024 00:06:37 -0600 Subject: [PATCH 126/451] test: longer Eventually timeout for TestWorker (#12822) --- op-supervisor/supervisor/backend/cross/worker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-supervisor/supervisor/backend/cross/worker_test.go b/op-supervisor/supervisor/backend/cross/worker_test.go index 03fda620350b..cc9d173a28d0 100644 --- a/op-supervisor/supervisor/backend/cross/worker_test.go +++ b/op-supervisor/supervisor/backend/cross/worker_test.go @@ -94,7 +94,7 @@ func TestWorker(t *testing.T) { w.StartBackground() require.Eventually(t, func() bool { return count == 10 - }, 2*time.Second, 100*time.Millisecond) + }, 10*time.Second, time.Second) // once the worker is closed, it stops running // and the count does not increment w.Close() From fd7d34ccb8f1aad58dbe1ff09fe0030858d1f4a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:29:28 -0700 Subject: [PATCH 127/451] dependabot(gomod): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#12816) Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 66c99169912c..0cf83e382d24 100644 --- a/go.mod +++ b/go.mod @@ -110,7 +110,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect diff --git a/go.sum b/go.sum index 3eaeda632c7b..3819b1e73fae 100644 --- a/go.sum +++ b/go.sum @@ -264,8 +264,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= From 6fbf9db6d57158797127516bc231dad480918289 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 5 Nov 2024 00:43:48 -0700 Subject: [PATCH 128/451] op-supervisor: fix data race in test (#12824) * op-supervisor: fix data race in test This test is flaky in CI. I believe it may be related to a data race: the `count` variable was being read from/written to across multiple threads which could lead to invalid data being read by the test. I verified that the data race was gone by running the test with `-race`. * Use >= rather than exact numbers --- .../supervisor/backend/cross/worker_test.go | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/op-supervisor/supervisor/backend/cross/worker_test.go b/op-supervisor/supervisor/backend/cross/worker_test.go index cc9d173a28d0..6f9d543fde1a 100644 --- a/op-supervisor/supervisor/backend/cross/worker_test.go +++ b/op-supervisor/supervisor/backend/cross/worker_test.go @@ -2,6 +2,7 @@ package cross import ( "context" + "sync/atomic" "testing" "time" @@ -13,20 +14,20 @@ import ( func TestWorker(t *testing.T) { logger := testlog.Logger(t, log.LevelDebug) t.Run("do work", func(t *testing.T) { - count := 0 + var count int32 w := NewWorker(logger, func(ctx context.Context) error { - count++ + atomic.AddInt32(&count, 1) return nil }) t.Cleanup(w.Close) // when ProcessWork is called, the workFn is called once require.NoError(t, w.ProcessWork()) - require.Equal(t, 1, count) + require.EqualValues(t, 1, atomic.LoadInt32(&count)) }) t.Run("background worker", func(t *testing.T) { - count := 0 + var count int32 w := NewWorker(logger, func(ctx context.Context) error { - count++ + atomic.AddInt32(&count, 1) return nil }) t.Cleanup(w.Close) @@ -36,13 +37,13 @@ func TestWorker(t *testing.T) { // the count should increment once w.StartBackground() require.Eventually(t, func() bool { - return count == 1 + return atomic.LoadInt32(&count) == 1 }, 2*time.Second, 100*time.Millisecond) }) t.Run("background worker OnNewData", func(t *testing.T) { - count := 0 + var count int32 w := NewWorker(logger, func(ctx context.Context) error { - count++ + atomic.AddInt32(&count, 1) return nil }) t.Cleanup(w.Close) @@ -52,55 +53,55 @@ func TestWorker(t *testing.T) { // the count should increment once w.StartBackground() require.Eventually(t, func() bool { - return count == 1 + return atomic.LoadInt32(&count) == 1 }, 2*time.Second, 100*time.Millisecond) // when OnNewData is called, the worker runs again w.OnNewData() require.Eventually(t, func() bool { - return count == 2 + return atomic.LoadInt32(&count) == 2 }, 2*time.Second, 100*time.Millisecond) // and due to the long poll duration, the worker does not run again require.Never(t, func() bool { - return count > 2 - }, 10*time.Second, 100*time.Millisecond) + return atomic.LoadInt32(&count) > 2 + }, time.Second, 100*time.Millisecond) }) t.Run("background fast poll", func(t *testing.T) { - count := 0 + var count int32 w := NewWorker(logger, func(ctx context.Context) error { - count++ + atomic.AddInt32(&count, 1) return nil }) t.Cleanup(w.Close) // set a long poll duration so the worker does not auto-run w.pollDuration = 100 * time.Millisecond // when StartBackground is called, the worker runs in the background - // the count should increment rapidly and reach 10 in 1 second + // the count should increment rapidly and reach at least 10 in 1 second w.StartBackground() require.Eventually(t, func() bool { - return count == 10 + return atomic.LoadInt32(&count) >= 10 }, 2*time.Second, 100*time.Millisecond) }) t.Run("close", func(t *testing.T) { - count := 0 + var count int32 w := NewWorker(logger, func(ctx context.Context) error { - count++ + atomic.AddInt32(&count, 1) return nil }) t.Cleanup(w.Close) // close on cleanup in case of early error // set a long poll duration so the worker does not auto-run w.pollDuration = 100 * time.Millisecond // when StartBackground is called, the worker runs in the background - // the count should increment rapidly and reach 10 in 1 second + // the count should increment rapidly and reach at least 10 in 1 second w.StartBackground() require.Eventually(t, func() bool { - return count == 10 + return atomic.LoadInt32(&count) >= 10 }, 10*time.Second, time.Second) // once the worker is closed, it stops running // and the count does not increment w.Close() - stopCount := count + stopCount := atomic.LoadInt32(&count) require.Never(t, func() bool { - return count != stopCount - }, 3*time.Second, 100*time.Millisecond) + return atomic.LoadInt32(&count) != stopCount + }, time.Second, 100*time.Millisecond) }) } From 4fa3ede7e7acee59aa08bd6c5c9d826e90605a62 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:49:26 -0300 Subject: [PATCH 129/451] test: fix superc20 interface test (#125) (#12825) --- .../test/L2/OptimismSuperchainERC20.t.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol index 8b83b2821796..e1dc1286d2bd 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol @@ -7,10 +7,11 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; -import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol"; import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; import { IERC165 } from "@openzeppelin/contracts-v5/utils/introspection/IERC165.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; import { IBeacon } from "@openzeppelin/contracts-v5/proxy/beacon/IBeacon.sol"; import { BeaconProxy } from "@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol"; import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; @@ -248,6 +249,8 @@ contract OptimismSuperchainERC20Test is Test { /// @notice Tests that the `supportsInterface` function returns true for the `ISuperchainERC20` interface. function test_supportInterface_succeeds() public view { assertTrue(optimismSuperchainERC20.supportsInterface(type(IERC165).interfaceId)); + assertTrue(optimismSuperchainERC20.supportsInterface(type(IERC20).interfaceId)); + assertTrue(optimismSuperchainERC20.supportsInterface(type(IERC7802).interfaceId)); assertTrue(optimismSuperchainERC20.supportsInterface(type(IOptimismSuperchainERC20).interfaceId)); } @@ -255,6 +258,8 @@ contract OptimismSuperchainERC20Test is Test { /// `ISuperchainERC20` one. function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { vm.assume(_interfaceId != type(IERC165).interfaceId); + vm.assume(_interfaceId != type(IERC20).interfaceId); + vm.assume(_interfaceId != type(IERC7802).interfaceId); vm.assume(_interfaceId != type(IOptimismSuperchainERC20).interfaceId); assertFalse(optimismSuperchainERC20.supportsInterface(_interfaceId)); } From 9948426d734fb275eed40cae9b629a61c0ef7ff1 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Wed, 6 Nov 2024 00:01:45 +0700 Subject: [PATCH 130/451] maint: remove proxyd and specs folders (#12826) Kills old folders that aren't used anymore. --- README.md | 3 +-- proxyd/README.md | 2 -- specs/README.md | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 proxyd/README.md delete mode 100644 specs/README.md diff --git a/README.md b/README.md index 789b0539d8ff..42a880f5f427 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,7 @@ The Optimism Immunefi program offers up to $2,000,042 for in-scope critical vuln ├── ops-bedrock: Bedrock devnet work ├── packages │ ├── contracts-bedrock: OP Stack smart contracts -├── proxyd: Configurable RPC request router and proxy -├── specs: Specs of the rollup starting at the Bedrock upgrade +├── semgrep: Semgrep rules and tests ## Development and Release Process diff --git a/proxyd/README.md b/proxyd/README.md deleted file mode 100644 index f44b815ab2dc..000000000000 --- a/proxyd/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# ⚠️ Important -This project has been moved to [ethereum-optimism/infra](https://github.com/ethereum-optimism/infra) diff --git a/specs/README.md b/specs/README.md deleted file mode 100644 index e75cc2b9f668..000000000000 --- a/specs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Specs Have Moved - -Specs have moved to a [dedicated repository](https://github.com/ethereum-optimism/specs). \ No newline at end of file From dec8c60acdada4286469e8fcb8123967be445991 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 5 Nov 2024 10:03:17 -0700 Subject: [PATCH 131/451] op-deployer: Add support for alt-DA deployments (#12798) * op-deployer: Add support for alt-DA deployments Gives users the ability to deploy an alt-DA chain by specifying an alt-DA config in their chain's intent. The chain will be deployed using OPCM, then an additional pipeline step will deploy the alt-DA challenge contracts. The owner of the challenge contract is set to the L1 proxy admin owner. To reflect the experimental nature of this feature, the field in the intent is prefixed with `Dangerous`. Users should not use this for production chains until we have performed further testing. This may not appear like an important feature on its surface. However, without it we cannot delete the legacy allocs files. Since it was low lift I figured I'd just knock it out, and get us one step closer to being able to rip out the legacy deployment scripts and tooling once and for all. * semgrep * forge fmt * label * flip args --- op-chain-ops/genesis/config.go | 12 +- op-chain-ops/script/cheatcodes_utilities.go | 11 + .../script/cheatcodes_utilities_test.go | 11 + op-deployer/pkg/deployer/apply.go | 5 + .../deployer/integration_test/apply_test.go | 43 +++ op-deployer/pkg/deployer/opcm/alt_da.go | 63 +++++ op-deployer/pkg/deployer/opcm/alt_da_test.go | 43 +++ op-deployer/pkg/deployer/pipeline/alt_da.go | 56 ++++ .../pkg/deployer/state/deploy_config.go | 5 + op-deployer/pkg/deployer/state/intent.go | 8 + op-deployer/pkg/deployer/state/state.go | 2 + .../scripts/deploy/DeployAltDA.s.sol | 199 +++++++++++++ .../test/opcm/DeployAltDA.t.sol | 266 ++++++++++++++++++ 13 files changed, 718 insertions(+), 6 deletions(-) create mode 100644 op-deployer/pkg/deployer/opcm/alt_da.go create mode 100644 op-deployer/pkg/deployer/opcm/alt_da_test.go create mode 100644 op-deployer/pkg/deployer/pipeline/alt_da.go create mode 100644 packages/contracts-bedrock/scripts/deploy/DeployAltDA.s.sol create mode 100644 packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index d89c124e6d31..7ed0a6e2f682 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -597,18 +597,18 @@ func (d *L2CoreDeployConfig) Check(log log.Logger) error { // AltDADeployConfig configures optional AltDA functionality. type AltDADeployConfig struct { // UseAltDA is a flag that indicates if the system is using op-alt-da - UseAltDA bool `json:"useAltDA"` + UseAltDA bool `json:"useAltDA" toml:"useAltDA"` // DACommitmentType specifies the allowed commitment - DACommitmentType string `json:"daCommitmentType"` + DACommitmentType string `json:"daCommitmentType" toml:"daCommitmentType"` // DAChallengeWindow represents the block interval during which the availability of a data commitment can be challenged. - DAChallengeWindow uint64 `json:"daChallengeWindow"` + DAChallengeWindow uint64 `json:"daChallengeWindow" toml:"daChallengeWindow"` // DAResolveWindow represents the block interval during which a data availability challenge can be resolved. - DAResolveWindow uint64 `json:"daResolveWindow"` + DAResolveWindow uint64 `json:"daResolveWindow" toml:"daResolveWindow"` // DABondSize represents the required bond size to initiate a data availability challenge. - DABondSize uint64 `json:"daBondSize"` + DABondSize uint64 `json:"daBondSize" toml:"daBondSize"` // DAResolverRefundPercentage represents the percentage of the resolving cost to be refunded to the resolver // such as 100 means 100% refund. - DAResolverRefundPercentage uint64 `json:"daResolverRefundPercentage"` + DAResolverRefundPercentage uint64 `json:"daResolverRefundPercentage" toml:"daResolverRefundPercentage"` } var _ ConfigChecker = (*AltDADeployConfig)(nil) diff --git a/op-chain-ops/script/cheatcodes_utilities.go b/op-chain-ops/script/cheatcodes_utilities.go index 022befa60627..4f7606d7e4fb 100644 --- a/op-chain-ops/script/cheatcodes_utilities.go +++ b/op-chain-ops/script/cheatcodes_utilities.go @@ -233,6 +233,17 @@ func (c *CheatCodesPrecompile) ParseTomlAddress_65e7c844(tomlStr string, key str panic("should never get here") } +func (c *CheatCodesPrecompile) ComputeCreate2Address_890c283b(salt, codeHash [32]byte) (common.Address, error) { + data := make([]byte, 1+20+32+32) + data[0] = 0xff + copy(data[1:], DeterministicDeployerAddress.Bytes()) + copy(data[1+20:], salt[:]) + copy(data[1+20+32:], codeHash[:]) + finalHash := crypto.Keccak256(data) + // Take the last 20 bytes of the hash to get the address + return common.BytesToAddress(finalHash[12:]), nil +} + // unsupported //func (c *CheatCodesPrecompile) CreateWallet() {} diff --git a/op-chain-ops/script/cheatcodes_utilities_test.go b/op-chain-ops/script/cheatcodes_utilities_test.go index 23936a10e344..4870ec8129db 100644 --- a/op-chain-ops/script/cheatcodes_utilities_test.go +++ b/op-chain-ops/script/cheatcodes_utilities_test.go @@ -57,3 +57,14 @@ func TestParseTomlAddress(t *testing.T) { require.NoError(t, err) require.Equal(t, common.HexToAddress("0xff4ce7b6a91a35c31d7d62b327d19617c8da6f23"), addr) } + +func TestComputeCreate2Address(t *testing.T) { + c := &CheatCodesPrecompile{} + var salt [32]byte + salt[31] = 'S' + var codeHash [32]byte + codeHash[31] = 'C' + addr, err := c.ComputeCreate2Address_890c283b(salt, codeHash) + require.NoError(t, err) + require.EqualValues(t, common.HexToAddress("0x2f29AF1b5a7083bf98C4A89976c2f17FF980735f"), addr) +} diff --git a/op-deployer/pkg/deployer/apply.go b/op-deployer/pkg/deployer/apply.go index f6eb38d11e08..bdfd5c55ea1e 100644 --- a/op-deployer/pkg/deployer/apply.go +++ b/op-deployer/pkg/deployer/apply.go @@ -236,6 +236,11 @@ func ApplyPipeline( return pipeline.DeployOPChainGenesisStrategy(env, intent, st, chainID) } }, + }, pipelineStage{ + fmt.Sprintf("deploy-alt-da-%s", chainID.Hex()), + func() error { + return pipeline.DeployAltDA(env, intent, st, chainID) + }, }, pipelineStage{ fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()), func() error { diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index f123953a3ea4..fa59c49dc8ac 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -11,6 +11,10 @@ import ( "testing" "time" + altda "github.com/ethereum-optimism/optimism/op-alt-da" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-chain-ops/script" @@ -404,6 +408,45 @@ func TestInteropDeployment(t *testing.T) { checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) } +func TestAltDADeployment(t *testing.T) { + op_e2e.InitParallel(t) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + env, bundle, intent, st := setupGenesisChain(t) + altDACfg := genesis.AltDADeployConfig{ + UseAltDA: true, + DACommitmentType: altda.KeccakCommitmentString, + DAChallengeWindow: 10, + DAResolveWindow: 10, + DABondSize: 100, + DAResolverRefundPercentage: 50, + } + intent.Chains[0].DangerousAltDAConfig = altDACfg + + require.NoError(t, deployer.ApplyPipeline( + ctx, + env, + bundle, + intent, + st, + )) + + chainState := st.Chains[0] + require.NotEmpty(t, chainState.DataAvailabilityChallengeProxyAddress) + require.NotEmpty(t, chainState.DataAvailabilityChallengeImplAddress) + + _, rollupCfg, err := inspect.GenesisAndRollup(st, chainState.ID) + require.NoError(t, err) + require.EqualValues(t, &rollup.AltDAConfig{ + CommitmentType: altda.KeccakCommitmentString, + DAChallengeWindow: altDACfg.DAChallengeWindow, + DAChallengeAddress: chainState.DataAvailabilityChallengeProxyAddress, + DAResolveWindow: altDACfg.DAResolveWindow, + }, rollupCfg.AltDAConfig) +} + func TestInvalidL2Genesis(t *testing.T) { op_e2e.InitParallel(t) diff --git a/op-deployer/pkg/deployer/opcm/alt_da.go b/op-deployer/pkg/deployer/opcm/alt_da.go new file mode 100644 index 000000000000..7c05a42a7a5a --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/alt_da.go @@ -0,0 +1,63 @@ +package opcm + +import ( + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum/go-ethereum/common" +) + +type DeployAltDAInput struct { + Salt common.Hash + ProxyAdmin common.Address + ChallengeContractOwner common.Address + ChallengeWindow *big.Int + ResolveWindow *big.Int + BondSize *big.Int + ResolverRefundPercentage *big.Int +} + +type DeployAltDAOutput struct { + DataAvailabilityChallengeProxy common.Address + DataAvailabilityChallengeImpl common.Address +} + +type DeployAltDAScript struct { + Run func(input, output common.Address) error +} + +func DeployAltDA( + host *script.Host, + input DeployAltDAInput, +) (DeployAltDAOutput, error) { + var output DeployAltDAOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress[*DeployAltDAInput](host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployAltDAInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployAltDAOutput](host, outputAddr, &output, + script.WithFieldSetter[*DeployAltDAOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployAltDAOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployAltDA" + deployScript, cleanupDeploy, err := script.WithScript[DeployAltDAScript](host, "DeployAltDA.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to laod %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/opcm/alt_da_test.go b/op-deployer/pkg/deployer/opcm/alt_da_test.go new file mode 100644 index 000000000000..20b93907ebea --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/alt_da_test.go @@ -0,0 +1,43 @@ +package opcm + +import ( + "math/big" + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestDeployAltDA(t *testing.T) { + _, artifacts := testutil.LocalArtifacts(t) + + host, err := env.DefaultScriptHost( + broadcaster.NoopBroadcaster(), + testlog.Logger(t, log.LevelInfo), + common.Address{'D'}, + artifacts, + 0, + ) + require.NoError(t, err) + + input := DeployAltDAInput{ + Salt: common.HexToHash("0x1234"), + ProxyAdmin: common.Address{'P'}, + ChallengeContractOwner: common.Address{'O'}, + ChallengeWindow: big.NewInt(100), + ResolveWindow: big.NewInt(200), + BondSize: big.NewInt(300), + ResolverRefundPercentage: big.NewInt(50), // must be < 100 + } + + output, err := DeployAltDA(host, input) + require.NoError(t, err) + + require.NotEmpty(t, output.DataAvailabilityChallengeProxy) + require.NotEmpty(t, output.DataAvailabilityChallengeImpl) +} diff --git a/op-deployer/pkg/deployer/pipeline/alt_da.go b/op-deployer/pkg/deployer/pipeline/alt_da.go new file mode 100644 index 000000000000..62796832c93c --- /dev/null +++ b/op-deployer/pkg/deployer/pipeline/alt_da.go @@ -0,0 +1,56 @@ +package pipeline + +import ( + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum/go-ethereum/common" +) + +func DeployAltDA(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error { + lgr := env.Logger.New("stage", "deploy-alt-da") + + chainIntent, err := intent.Chain(chainID) + if err != nil { + return fmt.Errorf("failed to get chain intent: %w", err) + } + + chainState, err := st.Chain(chainID) + if err != nil { + return fmt.Errorf("failed to get chain state: %w", err) + } + + if !shouldDeployAltDA(chainIntent, chainState) { + lgr.Info("alt-da deployment not needed") + return nil + } + + var dao opcm.DeployAltDAOutput + lgr.Info("deploying alt-da contracts") + dao, err = opcm.DeployAltDA(env.L1ScriptHost, opcm.DeployAltDAInput{ + Salt: st.Create2Salt, + ProxyAdmin: st.ImplementationsDeployment.OpcmProxyAddress, + ChallengeContractOwner: chainIntent.Roles.L1ProxyAdminOwner, + ChallengeWindow: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DAChallengeWindow), + ResolveWindow: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DAResolveWindow), + BondSize: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DABondSize), + ResolverRefundPercentage: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DAResolverRefundPercentage), + }) + if err != nil { + return fmt.Errorf("failed to deploy alt-da contracts: %w", err) + } + + chainState.DataAvailabilityChallengeProxyAddress = dao.DataAvailabilityChallengeProxy + chainState.DataAvailabilityChallengeImplAddress = dao.DataAvailabilityChallengeImpl + return nil +} + +func shouldDeployAltDA(chainIntent *state.ChainIntent, chainState *state.ChainState) bool { + if !chainIntent.DangerousAltDAConfig.UseAltDA { + return false + } + + return chainState.DataAvailabilityChallengeImplAddress == common.Address{} +} diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 15d5491ce8b8..1a03c21d7e94 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -119,6 +119,11 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, } } + if chainIntent.DangerousAltDAConfig.UseAltDA { + cfg.AltDADeployConfig = chainIntent.DangerousAltDAConfig + cfg.L1DependenciesConfig.DAChallengeProxy = chainState.DataAvailabilityChallengeProxyAddress + } + // The below dummy variables are set in order to allow the deploy // config to pass validation. The validation checks are useful to // ensure that the L2 is properly configured. They are not used by diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 681d166bc142..860944666e9e 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -4,6 +4,8 @@ import ( "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" @@ -163,6 +165,8 @@ type ChainIntent struct { Roles ChainRoles `json:"roles" toml:"roles"` DeployOverrides map[string]any `json:"deployOverrides" toml:"deployOverrides"` + + DangerousAltDAConfig genesis.AltDADeployConfig `json:"dangerousAltDAConfig,omitempty" toml:"dangerousAltDAConfig,omitempty"` } type ChainRoles struct { @@ -207,5 +211,9 @@ func (c *ChainIntent) Check() error { return fmt.Errorf("batcher must be set") } + if c.DangerousAltDAConfig.UseAltDA { + return c.DangerousAltDAConfig.Check(nil) + } + return nil } diff --git a/op-deployer/pkg/deployer/state/state.go b/op-deployer/pkg/deployer/state/state.go index 3df543c56cdd..e3974fa2a78c 100644 --- a/op-deployer/pkg/deployer/state/state.go +++ b/op-deployer/pkg/deployer/state/state.go @@ -95,6 +95,8 @@ type ChainState struct { PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"` DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"` + DataAvailabilityChallengeProxyAddress common.Address `json:"dataAvailabilityChallengeProxyAddress"` + DataAvailabilityChallengeImplAddress common.Address `json:"dataAvailabilityChallengeImplAddress"` Allocs *GzipData[foundry.ForgeAllocs] `json:"allocs"` diff --git a/packages/contracts-bedrock/scripts/deploy/DeployAltDA.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployAltDA.s.sol new file mode 100644 index 000000000000..a5071474926b --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployAltDA.s.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { Script } from "forge-std/Script.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; +import { Solarray } from "scripts/libraries/Solarray.sol"; + +contract DeployAltDAInput is BaseDeployIO { + bytes32 internal _salt; + IProxyAdmin internal _proxyAdmin; + address internal _challengeContractOwner; + uint256 internal _challengeWindow; + uint256 internal _resolveWindow; + uint256 internal _bondSize; + uint256 internal _resolverRefundPercentage; + + function set(bytes4 _sel, bytes32 _val) public { + if (_sel == this.salt.selector) _salt = _val; + else revert("DeployAltDAInput: unknown selector"); + } + + function set(bytes4 _sel, address _addr) public { + require(_addr != address(0), "DeployAltDAInput: cannot set zero address"); + if (_sel == this.proxyAdmin.selector) _proxyAdmin = IProxyAdmin(_addr); + else if (_sel == this.challengeContractOwner.selector) _challengeContractOwner = _addr; + else revert("DeployAltDAInput: unknown selector"); + } + + function set(bytes4 _sel, uint256 _val) public { + if (_sel == this.challengeWindow.selector) _challengeWindow = _val; + else if (_sel == this.resolveWindow.selector) _resolveWindow = _val; + else if (_sel == this.bondSize.selector) _bondSize = _val; + else if (_sel == this.resolverRefundPercentage.selector) _resolverRefundPercentage = _val; + else revert("DeployAltDAInput: unknown selector"); + } + + function salt() public view returns (bytes32) { + require(_salt != 0, "DeployAltDAInput: salt not set"); + return _salt; + } + + function proxyAdmin() public view returns (IProxyAdmin) { + require(address(_proxyAdmin) != address(0), "DeployAltDAInput: proxyAdmin not set"); + return _proxyAdmin; + } + + function challengeContractOwner() public view returns (address) { + require(_challengeContractOwner != address(0), "DeployAltDAInput: challengeContractOwner not set"); + return _challengeContractOwner; + } + + function challengeWindow() public view returns (uint256) { + require(_challengeWindow != 0, "DeployAltDAInput: challengeWindow not set"); + return _challengeWindow; + } + + function resolveWindow() public view returns (uint256) { + require(_resolveWindow != 0, "DeployAltDAInput: resolveWindow not set"); + return _resolveWindow; + } + + function bondSize() public view returns (uint256) { + require(_bondSize != 0, "DeployAltDAInput: bondSize not set"); + return _bondSize; + } + + function resolverRefundPercentage() public view returns (uint256) { + require(_resolverRefundPercentage != 0, "DeployAltDAInput: resolverRefundPercentage not set"); + return _resolverRefundPercentage; + } +} + +contract DeployAltDAOutput is BaseDeployIO { + IDataAvailabilityChallenge internal _dataAvailabilityChallengeProxy; + IDataAvailabilityChallenge internal _dataAvailabilityChallengeImpl; + + function set(bytes4 _sel, address _addr) public { + require(_addr != address(0), "DeployAltDAOutput: cannot set zero address"); + if (_sel == this.dataAvailabilityChallengeProxy.selector) { + _dataAvailabilityChallengeProxy = IDataAvailabilityChallenge(payable(_addr)); + } else if (_sel == this.dataAvailabilityChallengeImpl.selector) { + _dataAvailabilityChallengeImpl = IDataAvailabilityChallenge(payable(_addr)); + } else { + revert("DeployAltDAOutput: unknown selector"); + } + } + + function dataAvailabilityChallengeProxy() public view returns (IDataAvailabilityChallenge) { + DeployUtils.assertValidContractAddress(address(_dataAvailabilityChallengeProxy)); + return _dataAvailabilityChallengeProxy; + } + + function dataAvailabilityChallengeImpl() public view returns (IDataAvailabilityChallenge) { + DeployUtils.assertValidContractAddress(address(_dataAvailabilityChallengeImpl)); + return _dataAvailabilityChallengeImpl; + } +} + +contract DeployAltDA is Script { + function run(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + deployDataAvailabilityChallengeProxy(_dai, _dao); + deployDataAvailabilityChallengeImpl(_dai, _dao); + initializeDataAvailabilityChallengeProxy(_dai, _dao); + + checkOutput(_dai, _dao); + } + + function deployDataAvailabilityChallengeProxy(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + bytes32 salt = _dai.salt(); + vm.broadcast(msg.sender); + IProxy proxy = IProxy( + DeployUtils.create2({ + _name: "Proxy", + _salt: salt, + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + }) + ); + vm.label(address(proxy), "DataAvailabilityChallengeProxy"); + _dao.set(_dao.dataAvailabilityChallengeProxy.selector, address(proxy)); + } + + function deployDataAvailabilityChallengeImpl(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + bytes32 salt = _dai.salt(); + vm.broadcast(msg.sender); + IDataAvailabilityChallenge impl = IDataAvailabilityChallenge( + DeployUtils.create2({ + _name: "DataAvailabilityChallenge", + _salt: salt, + _args: DeployUtils.encodeConstructor(abi.encodeCall(IDataAvailabilityChallenge.__constructor__, ())) + }) + ); + vm.label(address(impl), "DataAvailabilityChallengeImpl"); + _dao.set(_dao.dataAvailabilityChallengeImpl.selector, address(impl)); + } + + function initializeDataAvailabilityChallengeProxy(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + IProxy proxy = IProxy(payable(address(_dao.dataAvailabilityChallengeProxy()))); + IDataAvailabilityChallenge impl = _dao.dataAvailabilityChallengeImpl(); + IProxyAdmin proxyAdmin = IProxyAdmin(payable(address(_dai.proxyAdmin()))); + + address contractOwner = _dai.challengeContractOwner(); + uint256 challengeWindow = _dai.challengeWindow(); + uint256 resolveWindow = _dai.resolveWindow(); + uint256 bondSize = _dai.bondSize(); + uint256 resolverRefundPercentage = _dai.resolverRefundPercentage(); + + vm.startBroadcast(msg.sender); + proxy.upgradeToAndCall( + address(impl), + abi.encodeCall( + IDataAvailabilityChallenge.initialize, + (contractOwner, challengeWindow, resolveWindow, bondSize, resolverRefundPercentage) + ) + ); + proxy.changeAdmin(address(proxyAdmin)); + vm.stopBroadcast(); + } + + function checkOutput(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + address[] memory addresses = Solarray.addresses( + address(_dao.dataAvailabilityChallengeProxy()), address(_dao.dataAvailabilityChallengeImpl()) + ); + DeployUtils.assertValidContractAddresses(addresses); + + assertValidDataAvailabilityChallengeProxy(_dai, _dao); + assertValidDataAvailabilityChallengeImpl(_dao); + } + + function assertValidDataAvailabilityChallengeProxy(DeployAltDAInput _dai, DeployAltDAOutput _dao) public { + DeployUtils.assertERC1967ImplementationSet(address(_dao.dataAvailabilityChallengeProxy())); + + IProxy proxy = IProxy(payable(address(_dao.dataAvailabilityChallengeProxy()))); + vm.prank(address(0)); + address admin = proxy.admin(); + require(admin == address(_dai.proxyAdmin()), "DACP-10"); + + DeployUtils.assertInitialized({ _contractAddress: address(proxy), _slot: 0, _offset: 0 }); + + vm.prank(address(0)); + address impl = proxy.implementation(); + require(impl == address(_dao.dataAvailabilityChallengeImpl()), "DACP-20"); + + IDataAvailabilityChallenge dac = _dao.dataAvailabilityChallengeProxy(); + require(dac.owner() == _dai.challengeContractOwner(), "DACP-30"); + require(dac.challengeWindow() == _dai.challengeWindow(), "DACP-40"); + require(dac.resolveWindow() == _dai.resolveWindow(), "DACP-50"); + require(dac.bondSize() == _dai.bondSize(), "DACP-60"); + require(dac.resolverRefundPercentage() == _dai.resolverRefundPercentage(), "DACP-70"); + } + + function assertValidDataAvailabilityChallengeImpl(DeployAltDAOutput _dao) public view { + IDataAvailabilityChallenge dac = _dao.dataAvailabilityChallengeImpl(); + DeployUtils.assertInitialized({ _contractAddress: address(dac), _slot: 0, _offset: 0 }); + } +} diff --git a/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol b/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol new file mode 100644 index 000000000000..e48a9b37a9f8 --- /dev/null +++ b/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { Test } from "forge-std/Test.sol"; + +import { DeployAltDAInput, DeployAltDAOutput, DeployAltDA } from "scripts/deploy/DeployAltDA.s.sol"; +import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; +import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +contract DeployAltDAInput_Test is Test { + DeployAltDAInput dai; + + // Define defaults + bytes32 salt = bytes32(uint256(1)); + address proxyAdminAddr = makeAddr("proxyAdmin"); + address challengeContractOwner = makeAddr("challengeContractOwner"); + uint256 challengeWindow = 100; + uint256 resolveWindow = 200; + uint256 bondSize = 1 ether; + uint256 resolverRefundPercentage = 10; + + function setUp() public { + dai = new DeployAltDAInput(); + } + + function test_set_succeeds() public { + dai.set(dai.salt.selector, salt); + dai.set(dai.proxyAdmin.selector, proxyAdminAddr); + dai.set(dai.challengeContractOwner.selector, challengeContractOwner); + dai.set(dai.challengeWindow.selector, challengeWindow); + dai.set(dai.resolveWindow.selector, resolveWindow); + dai.set(dai.bondSize.selector, bondSize); + dai.set(dai.resolverRefundPercentage.selector, resolverRefundPercentage); + + // Compare the default inputs to the getter methods + assertEq(salt, dai.salt(), "100"); + assertEq(proxyAdminAddr, address(dai.proxyAdmin()), "200"); + assertEq(challengeContractOwner, dai.challengeContractOwner(), "300"); + assertEq(challengeWindow, dai.challengeWindow(), "400"); + assertEq(resolveWindow, dai.resolveWindow(), "500"); + assertEq(bondSize, dai.bondSize(), "600"); + assertEq(resolverRefundPercentage, dai.resolverRefundPercentage(), "700"); + } + + function test_getters_whenNotSet_revert() public { + bytes memory expectedErr = "DeployAltDAInput: "; + + vm.expectRevert(abi.encodePacked(expectedErr, "salt not set")); + dai.salt(); + + vm.expectRevert(abi.encodePacked(expectedErr, "proxyAdmin not set")); + dai.proxyAdmin(); + + vm.expectRevert(abi.encodePacked(expectedErr, "challengeContractOwner not set")); + dai.challengeContractOwner(); + + vm.expectRevert(abi.encodePacked(expectedErr, "challengeWindow not set")); + dai.challengeWindow(); + + vm.expectRevert(abi.encodePacked(expectedErr, "resolveWindow not set")); + dai.resolveWindow(); + + vm.expectRevert(abi.encodePacked(expectedErr, "bondSize not set")); + dai.bondSize(); + + vm.expectRevert(abi.encodePacked(expectedErr, "resolverRefundPercentage not set")); + dai.resolverRefundPercentage(); + } + + function test_set_zeroAddress_reverts() public { + vm.expectRevert("DeployAltDAInput: cannot set zero address"); + dai.set(dai.proxyAdmin.selector, address(0)); + + vm.expectRevert("DeployAltDAInput: cannot set zero address"); + dai.set(dai.challengeContractOwner.selector, address(0)); + } + + function test_set_unknownSelector_reverts() public { + bytes4 unknownSelector = bytes4(keccak256("unknown()")); + + vm.expectRevert("DeployAltDAInput: unknown selector"); + dai.set(unknownSelector, bytes32(0)); + + vm.expectRevert("DeployAltDAInput: unknown selector"); + dai.set(unknownSelector, address(1)); + + vm.expectRevert("DeployAltDAInput: unknown selector"); + dai.set(unknownSelector, uint256(1)); + } +} + +contract DeployAltDAOutput_Test is Test { + DeployAltDAOutput dao; + + // Store contract references to avoid stack too deep + IDataAvailabilityChallenge internal dataAvailabilityChallengeImpl; + + function setUp() public { + dao = new DeployAltDAOutput(); + dataAvailabilityChallengeImpl = IDataAvailabilityChallenge(payable(makeAddr("dataAvailabilityChallengeImpl"))); + } + + function test_set_succeeds() public { + // Build the implementation with some bytecode + vm.etch(address(dataAvailabilityChallengeImpl), hex"01"); + + // Build proxy with implementation + (IProxy dataAvailabilityChallengeProxy) = + DeployUtils.buildERC1967ProxyWithImpl("dataAvailabilityChallengeProxy"); + + // Set the addresses + dao.set(dao.dataAvailabilityChallengeProxy.selector, address(dataAvailabilityChallengeProxy)); + dao.set(dao.dataAvailabilityChallengeImpl.selector, address(dataAvailabilityChallengeImpl)); + + // Verify the addresses were set correctly + assertEq(address(dataAvailabilityChallengeProxy), address(dao.dataAvailabilityChallengeProxy()), "100"); + assertEq(address(dataAvailabilityChallengeImpl), address(dao.dataAvailabilityChallengeImpl()), "200"); + } + + function test_getters_whenNotSet_revert() public { + vm.expectRevert("DeployUtils: zero address"); + dao.dataAvailabilityChallengeProxy(); + + vm.expectRevert("DeployUtils: zero address"); + dao.dataAvailabilityChallengeImpl(); + } + + function test_getters_whenAddrHasNoCode_reverts() public { + address emptyAddr = makeAddr("emptyAddr"); + bytes memory expectedErr = bytes(string.concat("DeployUtils: no code at ", vm.toString(emptyAddr))); + + dao.set(dao.dataAvailabilityChallengeProxy.selector, emptyAddr); + vm.expectRevert(expectedErr); + dao.dataAvailabilityChallengeProxy(); + + dao.set(dao.dataAvailabilityChallengeImpl.selector, emptyAddr); + vm.expectRevert(expectedErr); + dao.dataAvailabilityChallengeImpl(); + } + + function test_set_zeroAddress_reverts() public { + vm.expectRevert("DeployAltDAOutput: cannot set zero address"); + dao.set(dao.dataAvailabilityChallengeProxy.selector, address(0)); + + vm.expectRevert("DeployAltDAOutput: cannot set zero address"); + dao.set(dao.dataAvailabilityChallengeImpl.selector, address(0)); + } + + function test_set_unknownSelector_reverts() public { + bytes4 unknownSelector = bytes4(keccak256("unknown()")); + vm.expectRevert("DeployAltDAOutput: unknown selector"); + dao.set(unknownSelector, address(1)); + } +} + +contract DeployAltDA_Test is Test { + DeployAltDA deployer; + DeployAltDAInput dai; + DeployAltDAOutput dao; + + // Define defaults + bytes32 salt = bytes32(uint256(1)); + IProxyAdmin proxyAdmin; + address challengeContractOwner = makeAddr("challengeContractOwner"); + uint256 challengeWindow = 100; + uint256 resolveWindow = 200; + uint256 bondSize = 1 ether; + uint256 resolverRefundPercentage = 10; + + function setUp() public { + // Deploy the main contract and get input/output contracts + deployer = new DeployAltDA(); + (dai, dao) = _setupIOContracts(); + + // Setup proxyAdmin + proxyAdmin = IProxyAdmin( + DeployUtils.create1({ + _name: "ProxyAdmin", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (msg.sender))) + }) + ); + + // Set the default values + dai.set(dai.salt.selector, salt); + dai.set(dai.proxyAdmin.selector, address(proxyAdmin)); + dai.set(dai.challengeContractOwner.selector, challengeContractOwner); + dai.set(dai.challengeWindow.selector, challengeWindow); + dai.set(dai.resolveWindow.selector, resolveWindow); + dai.set(dai.bondSize.selector, bondSize); + dai.set(dai.resolverRefundPercentage.selector, resolverRefundPercentage); + } + + function _setupIOContracts() internal returns (DeployAltDAInput, DeployAltDAOutput) { + DeployAltDAInput _dai = new DeployAltDAInput(); + DeployAltDAOutput _dao = new DeployAltDAOutput(); + return (_dai, _dao); + } + + function test_run_succeeds() public { + deployer.run(dai, dao); + + // Verify everything is set up correctly + IDataAvailabilityChallenge dac = dao.dataAvailabilityChallengeProxy(); + assertTrue(address(dac).code.length > 0, "100"); + assertTrue(address(dao.dataAvailabilityChallengeImpl()).code.length > 0, "200"); + + // Check all initialization parameters + assertEq(dac.owner(), challengeContractOwner, "300"); + assertEq(dac.challengeWindow(), challengeWindow, "400"); + assertEq(dac.resolveWindow(), resolveWindow, "500"); + assertEq(dac.bondSize(), bondSize, "600"); + assertEq(dac.resolverRefundPercentage(), resolverRefundPercentage, "700"); + // Make sure the proxy admin is set correctly. + vm.prank(address(0)); + assertEq(IProxy(payable(address(dac))).admin(), address(proxyAdmin), "800"); + } + + function test_checkOutput_whenNotInitialized_reverts() public { + vm.expectRevert("DeployUtils: zero address"); + deployer.checkOutput(dai, dao); + } + + function test_checkOutput_whenProxyNotInitialized_reverts() public { + // Deploy but don't initialize + deployer.deployDataAvailabilityChallengeProxy(dai, dao); + deployer.deployDataAvailabilityChallengeImpl(dai, dao); + + vm.expectRevert("DeployUtils: zero address"); + deployer.checkOutput(dai, dao); + } + + function testFuzz_run_withDifferentParameters( + uint256 _challengeWindow, + uint256 _resolveWindow, + uint256 _bondSize, + uint256 _resolverRefundPercentage + ) + public + { + // Bound the values to reasonable ranges + _challengeWindow = bound(_challengeWindow, 1, 365 days); + _resolveWindow = bound(_resolveWindow, 1, 365 days); + _bondSize = bound(_bondSize, 0.1 ether, 100 ether); + _resolverRefundPercentage = bound(_resolverRefundPercentage, 1, 100); + + // Set the new values + dai.set(dai.salt.selector, salt); + dai.set(dai.proxyAdmin.selector, address(proxyAdmin)); + dai.set(dai.challengeWindow.selector, _challengeWindow); + dai.set(dai.resolveWindow.selector, _resolveWindow); + dai.set(dai.bondSize.selector, _bondSize); + dai.set(dai.resolverRefundPercentage.selector, _resolverRefundPercentage); + + // Run deployment + deployer.run(dai, dao); + + // Verify values + IDataAvailabilityChallenge dac = dao.dataAvailabilityChallengeProxy(); + assertEq(dac.challengeWindow(), _challengeWindow, "100"); + assertEq(dac.resolveWindow(), _resolveWindow, "200"); + assertEq(dac.bondSize(), _bondSize, "300"); + assertEq(dac.resolverRefundPercentage(), _resolverRefundPercentage, "400"); + } +} From 8565b8f6928380bcaf0e20b4b5ea7ef120841be9 Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 5 Nov 2024 11:32:29 -0600 Subject: [PATCH 132/451] Interop: Add error case for parent of start of database (#12818) * Add error case for parent of start of database * fix unit tests --- .../supervisor/backend/db/fromda/db.go | 9 ++- .../supervisor/backend/db/fromda/db_test.go | 59 +++++++++++++------ 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/op-supervisor/supervisor/backend/db/fromda/db.go b/op-supervisor/supervisor/backend/db/fromda/db.go index 5ee609ecdfe7..c4c98624d26d 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db.go +++ b/op-supervisor/supervisor/backend/db/fromda/db.go @@ -184,8 +184,13 @@ func (db *DB) PreviousDerivedFrom(derivedFrom eth.BlockID) (prevDerivedFrom type if self.derivedFrom.ID() != derivedFrom { return types.BlockSeal{}, fmt.Errorf("found %s, but expected %s: %w", self.derivedFrom, derivedFrom, types.ErrConflict) } - if selfIndex == 0 { // genesis block has a zeroed block as parent block - return types.BlockSeal{}, nil + if selfIndex == 0 { + // genesis block has a zeroed block as parent block + if self.derivedFrom.Number == 0 { + return types.BlockSeal{}, nil + } else { + return types.BlockSeal{}, fmt.Errorf("cannot find previous derived before start of database: %s", derivedFrom) + } } prev, err := db.readAt(selfIndex - 1) if err != nil { diff --git a/op-supervisor/supervisor/backend/db/fromda/db_test.go b/op-supervisor/supervisor/backend/db/fromda/db_test.go index fbcf82ab194e..8d6a273e1f98 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db_test.go +++ b/op-supervisor/supervisor/backend/db/fromda/db_test.go @@ -131,74 +131,95 @@ func toRef(seal types.BlockSeal, parentHash common.Hash) eth.BlockRef { } func TestSingleEntryDB(t *testing.T) { - expectedDerivedFrom := mockL1(1) + expectedDerivedFrom := mockL1(0) expectedDerived := mockL2(2) runDBTest(t, func(t *testing.T, db *DB, m *stubMetrics) { require.NoError(t, db.AddDerived(toRef(expectedDerivedFrom, mockL1(0).Hash), toRef(expectedDerived, mockL2(0).Hash))) }, func(t *testing.T, db *DB, m *stubMetrics) { - derivedFrom, derived, err := db.Latest() + // First + derivedFrom, derived, err := db.First() require.NoError(t, err) require.Equal(t, expectedDerivedFrom, derivedFrom) require.Equal(t, expectedDerived, derived) - derivedFrom, derived, err = db.First() + // Latest + derivedFrom, derived, err = db.Latest() require.NoError(t, err) require.Equal(t, expectedDerivedFrom, derivedFrom) require.Equal(t, expectedDerived, derived) + // FirstAfter Latest + _, _, err = db.FirstAfter(derivedFrom.ID(), derived.ID()) + require.ErrorIs(t, err, types.ErrFuture) + + // LastDerivedAt derived, err = db.LastDerivedAt(expectedDerivedFrom.ID()) require.NoError(t, err) require.Equal(t, expectedDerived, derived) + // LastDerivedAt with a non-existent block _, err = db.LastDerivedAt(eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerivedFrom.Number}) require.ErrorIs(t, err, types.ErrConflict) - // No block known, yet, after the given block pair - _, _, err = db.FirstAfter(derivedFrom.ID(), derived.ID()) - require.ErrorIs(t, err, types.ErrFuture) - - // Not after a non-existent block pair + // FirstAfter with a non-existent block (derived and derivedFrom) _, _, err = db.FirstAfter(eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerivedFrom.Number}, expectedDerived.ID()) require.ErrorIs(t, err, types.ErrConflict) _, _, err = db.FirstAfter(expectedDerivedFrom.ID(), eth.BlockID{Hash: common.Hash{0xaa}, Number: expectedDerived.Number}) require.ErrorIs(t, err, types.ErrConflict) + // DerivedFrom derivedFrom, err = db.DerivedFrom(expectedDerived.ID()) require.NoError(t, err) require.Equal(t, expectedDerivedFrom, derivedFrom) + // DerivedFrom with a non-existent block _, err = db.DerivedFrom(eth.BlockID{Hash: common.Hash{0xbb}, Number: expectedDerived.Number}) require.ErrorIs(t, err, types.ErrConflict) + // PreviousDerived prev, err := db.PreviousDerived(expectedDerived.ID()) require.NoError(t, err) require.Equal(t, types.BlockSeal{}, prev, "zeroed seal before first entry") - _, _, err = db.NextDerived(expectedDerived.ID()) - require.ErrorIs(t, err, types.ErrFuture) - - // if 1 was the first inserted entry, then we skipped 0 - _, _, err = db.NextDerived(mockL2(0).ID()) - require.ErrorIs(t, err, types.ErrSkipped) - + // PreviousDerivedFrom prev, err = db.PreviousDerivedFrom(expectedDerivedFrom.ID()) require.NoError(t, err) require.Equal(t, types.BlockSeal{}, prev, "zeroed seal before first entry") - _, err = db.NextDerivedFrom(expectedDerivedFrom.ID()) + // NextDerived + _, _, err = db.NextDerived(expectedDerived.ID()) require.ErrorIs(t, err, types.ErrFuture) - // if 1 was the first inserted entry, then we skipped 0 - _, err = db.NextDerivedFrom(mockL1(0).ID()) - require.ErrorIs(t, err, types.ErrSkipped) + // NextDerivedFrom + _, err = db.NextDerivedFrom(expectedDerivedFrom.ID()) + require.ErrorIs(t, err, types.ErrFuture) + // FirstAfter _, _, err = db.FirstAfter(expectedDerivedFrom.ID(), expectedDerived.ID()) require.ErrorIs(t, err, types.ErrFuture) }) } +func TestGap(t *testing.T) { + // mockL1 starts at block 1 to produce a gap + expectedDerivedFrom := mockL1(1) + // mockL2 starts at block 2 to produce a gap + expectedDerived := mockL2(2) + runDBTest(t, + func(t *testing.T, db *DB, m *stubMetrics) { + require.NoError(t, db.AddDerived(toRef(expectedDerivedFrom, mockL1(0).Hash), toRef(expectedDerived, mockL2(0).Hash))) + }, + func(t *testing.T, db *DB, m *stubMetrics) { + _, _, err := db.NextDerived(mockL2(0).ID()) + require.ErrorIs(t, err, types.ErrSkipped) + + _, err = db.NextDerivedFrom(mockL1(0).ID()) + require.ErrorIs(t, err, types.ErrSkipped) + }) +} + func TestThreeEntryDB(t *testing.T) { l1Block0 := mockL1(0) l1Block1 := mockL1(1) From 39e6e6f6ecdec8cf9d112020ed1179cb0a681e6c Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 5 Nov 2024 14:16:58 -0600 Subject: [PATCH 133/451] Handle first L1 Parent Ref in CandidateCrossSafe (#12830) --- .../supervisor/backend/cross/safe_update.go | 2 +- .../backend/cross/safe_update_test.go | 2 +- op-supervisor/supervisor/backend/db/query.go | 23 ++++++++++++------- op-supervisor/supervisor/types/types.go | 21 +++++++++++++++-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/op-supervisor/supervisor/backend/cross/safe_update.go b/op-supervisor/supervisor/backend/cross/safe_update.go index 6d74ca8ad2ba..bcda7c9f782b 100644 --- a/op-supervisor/supervisor/backend/cross/safe_update.go +++ b/op-supervisor/supervisor/backend/cross/safe_update.go @@ -57,7 +57,7 @@ func CrossSafeUpdate(ctx context.Context, logger log.Logger, chainID types.Chain if err != nil { return fmt.Errorf("cannot find parent-block of cross-safe: %w", err) } - crossSafeRef := currentCrossSafe.WithParent(parent.ID()) + crossSafeRef := currentCrossSafe.MustWithParent(parent.ID()) logger.Debug("Bumping cross-safe scope", "scope", newScope, "crossSafe", crossSafeRef) if err := d.UpdateCrossSafe(chainID, newScope, crossSafeRef); err != nil { return fmt.Errorf("failed to update cross-safe head with L1 scope increment to %s and repeat of L2 block %s: %w", candidateScope, crossSafeRef, err) diff --git a/op-supervisor/supervisor/backend/cross/safe_update_test.go b/op-supervisor/supervisor/backend/cross/safe_update_test.go index b3361c711bd0..3fc0ebba1388 100644 --- a/op-supervisor/supervisor/backend/cross/safe_update_test.go +++ b/op-supervisor/supervisor/backend/cross/safe_update_test.go @@ -102,7 +102,7 @@ func TestCrossSafeUpdate(t *testing.T) { require.NoError(t, err) require.Equal(t, chainID, updatingChain) require.Equal(t, newScope, updatingCandidateScope) - crossSafeRef := currentCrossSafe.WithParent(parent.ID()) + crossSafeRef := currentCrossSafe.MustWithParent(parent.ID()) require.Equal(t, crossSafeRef, updatingCandidate) }) t.Run("NextDerivedFrom returns error", func(t *testing.T) { diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index 867e4e71955b..bc3547cd023e 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -157,7 +157,7 @@ func (db *ChainsDB) CrossDerivedFromBlockRef(chainID types.ChainID, derived eth. if err != nil { return eth.BlockRef{}, err } - return res.WithParent(parent.ID()), nil + return res.MustWithParent(parent.ID()), nil } // Check calls the underlying logDB to determine if the given log entry exists at the given location. @@ -238,9 +238,16 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c if err != nil { return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find first local-safe block: %w", err) } - // First block has no parent - return derivedFrom.WithParent(eth.BlockID{}), - derived.WithParent(eth.BlockID{}), nil + // the first derivedFrom (L1 block) is unlikely to be the genesis block, + derivedFromRef, err := derivedFrom.WithParent(eth.BlockID{}) + if err != nil { + // if the first derivedFrom isn't the genesis block, just warn and continue anyway + db.logger.Warn("First DerivedFrom is not genesis block") + derivedFromRef = derivedFrom.ForceWithParent(eth.BlockID{}) + } + // the first derived must be the genesis block, panic otherwise + derivedRef := derived.MustWithParent(derivedFrom.ID()) + return derivedFromRef, derivedRef, nil } return eth.BlockRef{}, eth.BlockRef{}, err } @@ -256,13 +263,13 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c return eth.BlockRef{}, eth.BlockRef{}, err } - candidateRef := candidate.WithParent(crossDerived.ID()) + candidateRef := candidate.MustWithParent(crossDerived.ID()) parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID()) if err != nil { return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err) } - candidateFromRef := candidateFrom.WithParent(parentDerivedFrom.ID()) + candidateFromRef := candidateFrom.MustWithParent(parentDerivedFrom.ID()) // Allow increment of DA by 1, if we know the floor (due to local safety) is 1 ahead of the current cross-safe L1 scope. if candidateFrom.Number > crossDerivedFrom.Number+1 { @@ -273,7 +280,7 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", crossDerivedFrom, err) } - crossDerivedFromRef := crossDerivedFrom.WithParent(parent.ID()) + crossDerivedFromRef := crossDerivedFrom.MustWithParent(parent.ID()) return crossDerivedFromRef, eth.BlockRef{}, fmt.Errorf("candidate is from %s, while current scope is %s: %w", candidateFrom, crossDerivedFrom, types.ErrOutOfScope) @@ -306,7 +313,7 @@ func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID if err != nil { return eth.BlockRef{}, err } - return v.WithParent(derivedFrom), nil + return v.MustWithParent(derivedFrom), nil } // Safest returns the strongest safety level that can be guaranteed for the given log entry. diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index bac2c377bfd7..381d1fc3cbda 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -240,12 +240,29 @@ func (s BlockSeal) ID() eth.BlockID { return eth.BlockID{Hash: s.Hash, Number: s.Number} } -func (s BlockSeal) WithParent(parent eth.BlockID) eth.BlockRef { +func (s BlockSeal) MustWithParent(parent eth.BlockID) eth.BlockRef { + ref, err := s.WithParent(parent) + if err != nil { + panic(err) + } + return ref +} + +func (s BlockSeal) WithParent(parent eth.BlockID) (eth.BlockRef, error) { // prevent parent attachment if the parent is not the previous block, // and the block is not the genesis block if s.Number != parent.Number+1 && s.Number != 0 { - panic(fmt.Errorf("invalid parent block %s to combine with %s", parent, s)) + return eth.BlockRef{}, fmt.Errorf("invalid parent block %s to combine with %s", parent, s) } + return eth.BlockRef{ + Hash: s.Hash, + Number: s.Number, + ParentHash: parent.Hash, + Time: s.Timestamp, + }, nil +} + +func (s BlockSeal) ForceWithParent(parent eth.BlockID) eth.BlockRef { return eth.BlockRef{ Hash: s.Hash, Number: s.Number, From d627f1189666c5e4606a3180109791e846d73e32 Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 5 Nov 2024 15:08:59 -0600 Subject: [PATCH 134/451] interop: correct error in prior WithParent commit (#12832) --- op-supervisor/supervisor/backend/db/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index bc3547cd023e..e3040998859f 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -246,7 +246,7 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c derivedFromRef = derivedFrom.ForceWithParent(eth.BlockID{}) } // the first derived must be the genesis block, panic otherwise - derivedRef := derived.MustWithParent(derivedFrom.ID()) + derivedRef := derived.MustWithParent(eth.BlockID{}) return derivedFromRef, derivedRef, nil } return eth.BlockRef{}, eth.BlockRef{}, err From b20ca5db16da5dbe66f64ea97a2ac60915caa4ea Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 5 Nov 2024 22:59:56 -0600 Subject: [PATCH 135/451] Handle ErrParentTo from PreviousDerivedFrom (#12833) --- .../supervisor/backend/db/fromda/db.go | 3 ++- op-supervisor/supervisor/backend/db/query.go | 26 ++++++++++++++----- op-supervisor/supervisor/types/error.go | 3 +++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/op-supervisor/supervisor/backend/db/fromda/db.go b/op-supervisor/supervisor/backend/db/fromda/db.go index c4c98624d26d..48d8564ced20 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db.go +++ b/op-supervisor/supervisor/backend/db/fromda/db.go @@ -189,7 +189,8 @@ func (db *DB) PreviousDerivedFrom(derivedFrom eth.BlockID) (prevDerivedFrom type if self.derivedFrom.Number == 0 { return types.BlockSeal{}, nil } else { - return types.BlockSeal{}, fmt.Errorf("cannot find previous derived before start of database: %s", derivedFrom) + return types.BlockSeal{}, + fmt.Errorf("cannot find previous derived before start of database: %s (%w)", derivedFrom, types.ErrPreviousToFirst) } } prev, err := db.readAt(selfIndex - 1) diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index e3040998859f..bbee01d71b4d 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -154,7 +154,11 @@ func (db *ChainsDB) CrossDerivedFromBlockRef(chainID types.ChainID, derived eth. return eth.BlockRef{}, err } parent, err := xdb.PreviousDerivedFrom(res.ID()) - if err != nil { + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + return res.ForceWithParent(eth.BlockID{}), nil + } else if err != nil { return eth.BlockRef{}, err } return res.MustWithParent(parent.ID()), nil @@ -266,7 +270,11 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c candidateRef := candidate.MustWithParent(crossDerived.ID()) parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID()) - if err != nil { + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + parentDerivedFrom = types.BlockSeal{} + } else if err != nil { return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err) } candidateFromRef := candidateFrom.MustWithParent(parentDerivedFrom.ID()) @@ -275,12 +283,18 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c if candidateFrom.Number > crossDerivedFrom.Number+1 { // If we are not ready to process the candidate block, // then we need to stick to the current scope, so the caller can bump up from there. + var crossDerivedFromRef eth.BlockRef parent, err := lDB.PreviousDerivedFrom(crossDerivedFrom.ID()) - if err != nil { - return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", - crossDerivedFrom, err) + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + crossDerivedFromRef = crossDerivedFrom.ForceWithParent(eth.BlockID{}) + } else if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, + fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", crossDerivedFrom, err) + } else { + crossDerivedFromRef = crossDerivedFrom.MustWithParent(parent.ID()) } - crossDerivedFromRef := crossDerivedFrom.MustWithParent(parent.ID()) return crossDerivedFromRef, eth.BlockRef{}, fmt.Errorf("candidate is from %s, while current scope is %s: %w", candidateFrom, crossDerivedFrom, types.ErrOutOfScope) diff --git a/op-supervisor/supervisor/types/error.go b/op-supervisor/supervisor/types/error.go index b4e1c57ba908..fc493fb87f91 100644 --- a/op-supervisor/supervisor/types/error.go +++ b/op-supervisor/supervisor/types/error.go @@ -20,6 +20,9 @@ var ( // ErrOutOfScope is when data is accessed, but access is not allowed, because of a limited scope. // E.g. when limiting scope to L2 blocks derived from a specific subset of the L1 chain. ErrOutOfScope = errors.New("out of scope") + // ErrPreviousToFirst is when you try to get the previous block of the first block + // E.g. when calling PreviousDerivedFrom on the first L1 block in the DB. + ErrPreviousToFirst = errors.New("cannot get parent of first block in the database") // ErrUnknownChain is when a chain is unknown, not in the dependency set. ErrUnknownChain = errors.New("unknown chain") // ErrNoRPCSource happens when a sub-service needs an RPC data source, but is not configured with one. From 4052ab925c5d33f6f096591e92fde62ef5378bec Mon Sep 17 00:00:00 2001 From: George Knee Date: Wed, 6 Nov 2024 13:37:35 +0700 Subject: [PATCH 136/451] ensure we increase the pending_blocks metric when a channel times out (#12810) In this situation, the blocks get another chance to be put into a channel, when the metric will be decremented. This leads to inaccurate (even negative) metrics. --- op-batcher/batcher/channel_manager.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 887c51f4ebf2..4bfbd3124cec 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -115,11 +115,11 @@ func (s *channelManager) TxConfirmed(_id txID, inclusionBlock eth.BlockID) { id := _id.String() if channel, ok := s.txChannels[id]; ok { delete(s.txChannels, id) - done, blocks := channel.TxConfirmed(id, inclusionBlock) + done, blocksToRequeue := channel.TxConfirmed(id, inclusionBlock) if done { - s.removePendingChannel(channel) - if len(blocks) > 0 { - s.blocks.Prepend(blocks...) + for _, b := range blocksToRequeue { + s.blocks.Prepend(b) + s.metr.RecordL2BlockInPendingQueue(b) } } } else { @@ -504,9 +504,8 @@ func (s *channelManager) Requeue(newCfg ChannelConfig) { } // We put the blocks back at the front of the queue: - s.blocks.Prepend(blocksToRequeue...) - for _, b := range blocksToRequeue { + s.blocks.Prepend(b) s.metr.RecordL2BlockInPendingQueue(b) } From dd3ec4c9a0bb1a7a2cf652e45849e2e474ce0c41 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Tue, 5 Nov 2024 23:10:00 -0800 Subject: [PATCH 137/451] op-batcher: control loop to throttle DA usage (#12735) * implement throttling feedback loop based on the amount of data pending for DA settlement * make ThrottleInterval of 0 indicate do not start throttling loop * use modular batcher e2esys config * disable batcher throttling by default --------- Co-authored-by: Sebastian Stammler --- op-batcher/batcher/channel_manager.go | 14 ++ op-batcher/batcher/config.go | 18 +++ op-batcher/batcher/config_test.go | 7 +- op-batcher/batcher/driver.go | 133 ++++++++++++---- op-batcher/batcher/service.go | 22 +++ op-batcher/flags/flags.go | 35 +++++ op-batcher/metrics/metrics.go | 55 +++++-- op-batcher/metrics/noop.go | 14 ++ op-e2e/e2eutils/geth/geth.go | 12 +- op-e2e/system/da/brotli_batcher_test.go | 3 +- op-e2e/system/da/da_throttling_test.go | 192 ++++++++++++++++++++++++ op-e2e/system/e2esys/setup.go | 38 ++++- op-service/dial/ethclient_interface.go | 2 + op-service/metrics/factory.go | 10 ++ op-service/testutils/mock_eth_client.go | 6 + ops-bedrock/l2-op-geth-entrypoint.sh | 4 +- 16 files changed, 506 insertions(+), 59 deletions(-) create mode 100644 op-e2e/system/da/da_throttling_test.go diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 4bfbd3124cec..25900906d0dd 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "math" "sync" "github.com/ethereum-optimism/optimism/op-batcher/metrics" @@ -516,3 +517,16 @@ func (s *channelManager) Requeue(newCfg ChannelConfig) { // to pick up the new ChannelConfig s.defaultCfg = newCfg } + +// PendingDABytes returns the current number of bytes pending to be written to the DA layer (from blocks fetched from L2 +// but not yet in a channel). +func (s *channelManager) PendingDABytes() int64 { + f := s.metr.PendingDABytes() + if f >= math.MaxInt64 { + return math.MaxInt64 + } + if f <= math.MinInt64 { + return math.MinInt64 + } + return int64(f) +} diff --git a/op-batcher/batcher/config.go b/op-batcher/batcher/config.go index ac8bad7791a7..82be687af325 100644 --- a/op-batcher/batcher/config.go +++ b/op-batcher/batcher/config.go @@ -96,6 +96,19 @@ type CLIConfig struct { // ActiveSequencerCheckDuration is the duration between checks to determine the active sequencer endpoint. ActiveSequencerCheckDuration time.Duration + // ThrottleInterval is the interval between notifying the block builder of the latest DA throttling state, or 0 to + // disable notifications entirely (only recommended for testing). + ThrottleInterval time.Duration + // ThrottleThreshold is the number of pending bytes beyond which the batcher will start throttling future bytes + // written to DA. + ThrottleThreshold uint64 + // ThrottleTxSize is the DA size of a transaction to start throttling when we are over the throttling threshold. + ThrottleTxSize uint64 + // ThrottleBlockSize is the total per-block DA limit to start imposing on block building when we are over the throttling threshold. + ThrottleBlockSize uint64 + // ThrottleAlwaysBlockSize is the total per-block DA limit to always imposing on block building. + ThrottleAlwaysBlockSize uint64 + // TestUseMaxTxSizeForBlobs allows to set the blob size with MaxL1TxSize. // Should only be used for testing purposes. TestUseMaxTxSizeForBlobs bool @@ -195,5 +208,10 @@ func NewConfig(ctx *cli.Context) *CLIConfig { PprofConfig: oppprof.ReadCLIConfig(ctx), RPC: oprpc.ReadCLIConfig(ctx), AltDA: altda.ReadCLIConfig(ctx), + ThrottleThreshold: ctx.Uint64(flags.ThrottleThresholdFlag.Name), + ThrottleInterval: ctx.Duration(flags.ThrottleIntervalFlag.Name), + ThrottleTxSize: ctx.Uint64(flags.ThrottleTxSizeFlag.Name), + ThrottleBlockSize: ctx.Uint64(flags.ThrottleBlockSizeFlag.Name), + ThrottleAlwaysBlockSize: ctx.Uint64(flags.ThrottleAlwaysBlockSizeFlag.Name), } } diff --git a/op-batcher/batcher/config_test.go b/op-batcher/batcher/config_test.go index 4b90ebaccb68..9ce1ab4628fa 100644 --- a/op-batcher/batcher/config_test.go +++ b/op-batcher/batcher/config_test.go @@ -38,8 +38,11 @@ func validBatcherConfig() batcher.CLIConfig { MetricsConfig: metrics.DefaultCLIConfig(), PprofConfig: oppprof.DefaultCLIConfig(), // The compressor config is not checked in config.Check() - RPC: rpc.DefaultCLIConfig(), - CompressionAlgo: derive.Zlib, + RPC: rpc.DefaultCLIConfig(), + CompressionAlgo: derive.Zlib, + ThrottleThreshold: 0, // no DA throttling + ThrottleInterval: 12 * time.Second, + ThrottleTxSize: 0, } } diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index b31dd29e2e05..000fb89d16f1 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/txmgr" "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/txpool" "github.com/ethereum/go-ethereum/core/types" @@ -34,6 +35,7 @@ var ( }, }, } + SetMaxDASizeMethod = "miner_setMaxDASize" ) type txRef struct { @@ -100,6 +102,8 @@ type BatchSubmitter struct { killCtx context.Context cancelKillCtx context.CancelFunc + l2BlockAdded chan struct{} // notifies the throttling loop whenever an l2 block is added + mutex sync.Mutex running bool @@ -290,6 +294,12 @@ func (l *BatchSubmitter) loadBlockIntoState(ctx context.Context, blockNumber uin return nil, fmt.Errorf("adding L2 block to state: %w", err) } + // notify the throttling loop it may be time to initiate throttling without blocking + select { + case l.l2BlockAdded <- struct{}{}: + default: + } + l.Log.Info("Added L2 block to local state", "block", eth.ToBlockID(block), "tx_count", len(block.Transactions()), "time", block.Time()) return block, nil } @@ -384,7 +394,6 @@ const ( func (l *BatchSubmitter) loop() { defer l.wg.Done() - receiptsCh := make(chan txmgr.TxReceipt[txRef]) queue := txmgr.NewQueue[txRef](l.killCtx, l.Txmgr, l.Config.MaxPendingTransactions) daGroup := &errgroup.Group{} // errgroup with limit of 0 means no goroutine is able to run concurrently, @@ -393,37 +402,26 @@ func (l *BatchSubmitter) loop() { daGroup.SetLimit(int(l.Config.MaxConcurrentDARequests)) } - // start the receipt/result processing loop - receiptLoopDone := make(chan struct{}) - defer close(receiptLoopDone) // shut down receipt loop - l.txpoolMutex.Lock() l.txpoolState = TxpoolGood l.txpoolMutex.Unlock() - go func() { - for { - select { - case r := <-receiptsCh: - l.txpoolMutex.Lock() - if errors.Is(r.Err, txpool.ErrAlreadyReserved) && l.txpoolState == TxpoolGood { - l.txpoolState = TxpoolBlocked - l.txpoolBlockedBlob = r.ID.isBlob - l.Log.Info("incompatible tx in txpool", "is_blob", r.ID.isBlob) - } else if r.ID.isCancel && l.txpoolState == TxpoolCancelPending { - // Set state to TxpoolGood even if the cancellation transaction ended in error - // since the stuck transaction could have cleared while we were waiting. - l.txpoolState = TxpoolGood - l.Log.Info("txpool may no longer be blocked", "err", r.Err) - } - l.txpoolMutex.Unlock() - l.Log.Info("Handling receipt", "id", r.ID) - l.handleReceipt(r) - case <-receiptLoopDone: - l.Log.Info("Receipt processing loop done") - return - } - } - }() + + // start the receipt/result processing loop + receiptsLoopDone := make(chan struct{}) + defer close(receiptsLoopDone) // shut down receipt loop + l.l2BlockAdded = make(chan struct{}) + defer close(l.l2BlockAdded) + receiptsCh := make(chan txmgr.TxReceipt[txRef]) + go l.processReceiptsLoop(receiptsCh, receiptsLoopDone) + + // DA throttling loop should always be started except for testing (indicated by ThrottleInterval == 0) + if l.Config.ThrottleInterval > 0 { + throttlingLoopDone := make(chan struct{}) + defer close(throttlingLoopDone) + go l.throttlingLoop(throttlingLoopDone) + } else { + l.Log.Warn("Throttling loop is DISABLED due to 0 throttle-interval. This should not be disabled in prod.") + } ticker := time.NewTicker(l.Config.PollInterval) defer ticker.Stop() @@ -492,6 +490,83 @@ func (l *BatchSubmitter) loop() { } } +func (l *BatchSubmitter) processReceiptsLoop(receiptsCh chan txmgr.TxReceipt[txRef], receiptsLoopDone chan struct{}) { + l.Log.Info("Starting receipts processing loop") + for { + select { + case r := <-receiptsCh: + l.txpoolMutex.Lock() + if errors.Is(r.Err, txpool.ErrAlreadyReserved) && l.txpoolState == TxpoolGood { + l.txpoolState = TxpoolBlocked + l.txpoolBlockedBlob = r.ID.isBlob + l.Log.Info("incompatible tx in txpool", "is_blob", r.ID.isBlob) + } else if r.ID.isCancel && l.txpoolState == TxpoolCancelPending { + // Set state to TxpoolGood even if the cancellation transaction ended in error + // since the stuck transaction could have cleared while we were waiting. + l.txpoolState = TxpoolGood + l.Log.Info("txpool may no longer be blocked", "err", r.Err) + } + l.txpoolMutex.Unlock() + l.Log.Info("Handling receipt", "id", r.ID) + l.handleReceipt(r) + case <-receiptsLoopDone: + l.Log.Info("Receipts processing loop done") + return + } + } +} + +// throttlingLoop monitors the backlog in bytes we need to make available, and appropriately enables or disables +// throttling of incoming data prevent the backlog from growing too large. By looping & calling the miner API setter +// continuously, we ensure the engine currently in use is always going to be reset to the proper throttling settings +// even in the event of sequencer failover. +func (l *BatchSubmitter) throttlingLoop(throttlingLoopDone chan struct{}) { + l.Log.Info("Starting DA throttling loop") + ticker := time.NewTicker(l.Config.ThrottleInterval) + defer ticker.Stop() + + updateParams := func() { + ctx, cancel := context.WithTimeout(l.shutdownCtx, l.Config.NetworkTimeout) + defer cancel() + cl, err := l.EndpointProvider.EthClient(ctx) + if err != nil { + l.Log.Error("Can't reach sequencer execution RPC", "err", err) + return + } + pendingBytes := l.state.PendingDABytes() + maxTxSize := uint64(0) + maxBlockSize := l.Config.ThrottleAlwaysBlockSize + if pendingBytes > int64(l.Config.ThrottleThreshold) { + l.Log.Warn("Pending bytes over limit, throttling DA", "bytes", pendingBytes, "limit", l.Config.ThrottleThreshold) + maxTxSize = l.Config.ThrottleTxSize + if maxBlockSize == 0 || (l.Config.ThrottleBlockSize != 0 && l.Config.ThrottleBlockSize < maxBlockSize) { + maxBlockSize = l.Config.ThrottleBlockSize + } + } + var success bool + if err := cl.Client().CallContext( + ctx, &success, SetMaxDASizeMethod, hexutil.Uint64(maxTxSize), hexutil.Uint64(maxBlockSize)); err != nil { + l.Log.Error("SetMaxDASize rpc failed", "err", err) + return + } + if !success { + l.Log.Error("Result of SetMaxDASize was false") + } + } + + for { + select { + case <-l.l2BlockAdded: + updateParams() + case <-ticker.C: + updateParams() + case <-throttlingLoopDone: + l.Log.Info("DA throttling loop done") + return + } + } +} + // waitNodeSync Check to see if there was a batcher tx sent recently that // still needs more block confirmations before being considered finalized func (l *BatchSubmitter) waitNodeSync() error { diff --git a/op-batcher/batcher/service.go b/op-batcher/batcher/service.go index 90a85cc4ee48..1b150642261f 100644 --- a/op-batcher/batcher/service.go +++ b/op-batcher/batcher/service.go @@ -44,6 +44,11 @@ type BatcherConfig struct { WaitNodeSync bool CheckRecentTxsDepth int + + // For throttling DA. See CLIConfig in config.go for details on these parameters. + ThrottleThreshold, ThrottleTxSize uint64 + ThrottleBlockSize, ThrottleAlwaysBlockSize uint64 + ThrottleInterval time.Duration } // BatcherService represents a full batch-submitter instance and its resources, @@ -101,6 +106,13 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, version string, bs.NetworkTimeout = cfg.TxMgrConfig.NetworkTimeout bs.CheckRecentTxsDepth = cfg.CheckRecentTxsDepth bs.WaitNodeSync = cfg.WaitNodeSync + + bs.ThrottleThreshold = cfg.ThrottleThreshold + bs.ThrottleTxSize = cfg.ThrottleTxSize + bs.ThrottleBlockSize = cfg.ThrottleBlockSize + bs.ThrottleAlwaysBlockSize = cfg.ThrottleAlwaysBlockSize + bs.ThrottleInterval = cfg.ThrottleInterval + if err := bs.initRPCClients(ctx, cfg); err != nil { return err } @@ -457,3 +469,13 @@ func (bs *BatcherService) TestDriver() *TestBatchSubmitter { BatchSubmitter: bs.driver, } } + +// ThrottlingTestDriver returns a handler for the batch-submitter driver element that is in "always throttle" mode, for +// use only in testing. +func (bs *BatcherService) ThrottlingTestDriver() *TestBatchSubmitter { + tbs := &TestBatchSubmitter{ + BatchSubmitter: bs.driver, + } + tbs.BatchSubmitter.state.metr = new(metrics.ThrottlingMetrics) + return tbs +} diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 3fe66f33981b..538b70b6c9fa 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -156,6 +156,36 @@ var ( Value: false, EnvVars: prefixEnvVars("WAIT_NODE_SYNC"), } + ThrottleIntervalFlag = &cli.DurationFlag{ + Name: "throttle-interval", + Usage: "Interval between potential DA throttling actions. Zero (default) disables throttling. " + + "Recommended to be set to L2 block time if enabling.", + EnvVars: prefixEnvVars("THROTTLE_INTERVAL"), + } + ThrottleThresholdFlag = &cli.IntFlag{ + Name: "throttle-threshold", + Usage: "The threshold on pending-blocks-bytes-current beyond which the batcher will instruct the block builder to start throttling transactions with larger DA demands", + Value: 1_000_000, + EnvVars: prefixEnvVars("THROTTLE_THRESHOLD"), + } + ThrottleTxSizeFlag = &cli.IntFlag{ + Name: "throttle-tx-size", + Usage: "The DA size of transactions to start throttling when we are over the throttle threshold", + Value: 300, // most transactions compress to under 300 bytes. TODO: compute exact distribution + EnvVars: prefixEnvVars("THROTTLE_TX_SIZE"), + } + ThrottleBlockSizeFlag = &cli.IntFlag{ + Name: "throttle-block-size", + Usage: "The total DA limit to start imposing on block building when we are over the throttle threshold", + Value: 21_000, // at least 70 transactions per block of up to 300 compressed bytes each. + EnvVars: prefixEnvVars("THROTTLE_BLOCK_SIZE"), + } + ThrottleAlwaysBlockSizeFlag = &cli.IntFlag{ + Name: "throttle-always-block-size", + Usage: "The total DA limit to start imposing on block building at all times", + Value: 130_000, // should be larger than the builder's max-l2-tx-size to prevent endlessly throttling some txs + EnvVars: prefixEnvVars("THROTTLE_ALWAYS_BLOCK_SIZE"), + } // Legacy Flags SequencerHDPathFlag = txmgr.SequencerHDPathFlag ) @@ -184,6 +214,11 @@ var optionalFlags = []cli.Flag{ DataAvailabilityTypeFlag, ActiveSequencerCheckDurationFlag, CompressionAlgoFlag, + ThrottleThresholdFlag, + ThrottleIntervalFlag, + ThrottleTxSizeFlag, + ThrottleBlockSizeFlag, + ThrottleAlwaysBlockSizeFlag, } func init() { diff --git a/op-batcher/metrics/metrics.go b/op-batcher/metrics/metrics.go index 417f927ee6f6..6443e489bd46 100644 --- a/op-batcher/metrics/metrics.go +++ b/op-batcher/metrics/metrics.go @@ -2,6 +2,7 @@ package metrics import ( "io" + "sync/atomic" "github.com/prometheus/client_golang/prometheus" @@ -49,6 +50,8 @@ type Metricer interface { RecordBlobUsedBytes(num int) Document() []opmetrics.DocumentedMetric + + PendingDABytes() float64 } type Metrics struct { @@ -69,7 +72,11 @@ type Metrics struct { pendingBlocksCount prometheus.GaugeVec pendingBlocksBytesTotal prometheus.Counter pendingBlocksBytesCurrent prometheus.Gauge - blocksAddedCount prometheus.Gauge + + pendingDABytes int64 + pendingDABytesGaugeFunc prometheus.GaugeFunc + + blocksAddedCount prometheus.Gauge channelInputBytes prometheus.GaugeVec channelReadyBytes prometheus.Gauge @@ -99,7 +106,7 @@ func NewMetrics(procName string) *Metrics { registry := opmetrics.NewRegistry() factory := opmetrics.With(registry) - return &Metrics{ + m := &Metrics{ ns: ns, registry: registry, factory: factory, @@ -143,7 +150,6 @@ func NewMetrics(procName string) *Metrics { Name: "blocks_added_count", Help: "Total number of blocks added to current channel.", }), - channelInputBytes: *factory.NewGaugeVec(prometheus.GaugeOpts{ Namespace: ns, Name: "input_bytes", @@ -194,6 +200,13 @@ func NewMetrics(procName string) *Metrics { batcherTxEvs: opmetrics.NewEventVec(factory, ns, "", "batcher_tx", "BatcherTx", []string{"stage"}), } + m.pendingDABytesGaugeFunc = factory.NewGaugeFunc(prometheus.GaugeOpts{ + Namespace: ns, + Name: "pending_da_bytes", + Help: "The estimated amount of data currently pending to be written to the DA layer (from blocks fetched from L2 but not yet in a channel).", + }, m.PendingDABytes) + + return m } func (m *Metrics) Registry() *prometheus.Registry { @@ -204,6 +217,12 @@ func (m *Metrics) Document() []opmetrics.DocumentedMetric { return m.factory.Document() } +// PendingDABytes returns the current number of bytes pending to be written to the DA layer (from blocks fetched from L2 +// but not yet in a channel). +func (m *Metrics) PendingDABytes() float64 { + return float64(atomic.LoadInt64(&m.pendingDABytes)) +} + func (m *Metrics) StartBalanceMetrics(l log.Logger, client *ethclient.Client, account common.Address) io.Closer { return opmetrics.LaunchBalanceMetrics(l, m.registry, m.ns, client, account) } @@ -278,14 +297,16 @@ func (m *Metrics) RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, } func (m *Metrics) RecordL2BlockInPendingQueue(block *types.Block) { - size := float64(estimateBatchSize(block)) - m.pendingBlocksBytesTotal.Add(size) - m.pendingBlocksBytesCurrent.Add(size) + daSize, rawSize := estimateBatchSize(block) + m.pendingBlocksBytesTotal.Add(float64(rawSize)) + m.pendingBlocksBytesCurrent.Add(float64(rawSize)) + atomic.AddInt64(&m.pendingDABytes, int64(daSize)) } func (m *Metrics) RecordL2BlockInChannel(block *types.Block) { - size := float64(estimateBatchSize(block)) - m.pendingBlocksBytesCurrent.Add(-1 * size) + daSize, rawSize := estimateBatchSize(block) + m.pendingBlocksBytesCurrent.Add(-1.0 * float64(rawSize)) + atomic.AddInt64(&m.pendingDABytes, -1*int64(daSize)) // Refer to RecordL2BlocksAdded to see the current + count of bytes added to a channel } @@ -318,16 +339,22 @@ func (m *Metrics) RecordBlobUsedBytes(num int) { m.blobUsedBytes.Observe(float64(num)) } -// estimateBatchSize estimates the size of the batch -func estimateBatchSize(block *types.Block) uint64 { - size := uint64(70) // estimated overhead of batch metadata +// estimateBatchSize returns the estimated size of the block in a batch both with compression ('daSize') and without +// ('rawSize'). +func estimateBatchSize(block *types.Block) (daSize, rawSize uint64) { + daSize = uint64(70) // estimated overhead of batch metadata + rawSize = uint64(70) for _, tx := range block.Transactions() { - // Don't include deposit transactions in the batch. + // Deposit transactions are not included in batches if tx.IsDepositTx() { continue } + bigSize := tx.RollupCostData().EstimatedDASize() + if bigSize.IsUint64() { // this should always be true, but if not just ignore + daSize += bigSize.Uint64() + } // Add 2 for the overhead of encoding the tx bytes in a RLP list - size += tx.Size() + 2 + rawSize += tx.Size() + 2 } - return size + return } diff --git a/op-batcher/metrics/noop.go b/op-batcher/metrics/noop.go index 36594efe47c7..92711ee883d9 100644 --- a/op-batcher/metrics/noop.go +++ b/op-batcher/metrics/noop.go @@ -2,6 +2,7 @@ package metrics import ( "io" + "math" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -46,3 +47,16 @@ func (*noopMetrics) RecordBlobUsedBytes(int) {} func (*noopMetrics) StartBalanceMetrics(log.Logger, *ethclient.Client, common.Address) io.Closer { return nil } +func (nm *noopMetrics) PendingDABytes() float64 { + return 0.0 +} + +// ThrottlingMetrics is a noopMetrics that always returns a max value for PendingDABytes, to use in testing batcher +// backlog throttling. +type ThrottlingMetrics struct { + noopMetrics +} + +func (nm *ThrottlingMetrics) PendingDABytes() float64 { + return math.MaxFloat64 +} diff --git a/op-e2e/e2eutils/geth/geth.go b/op-e2e/e2eutils/geth/geth.go index de5b12b77d3c..b210949803bb 100644 --- a/op-e2e/e2eutils/geth/geth.go +++ b/op-e2e/e2eutils/geth/geth.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool/blobpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" @@ -49,8 +50,8 @@ func InitL1(blockTime uint64, finalizedDistance uint64, genesis *core.Genesis, c HTTPPort: 0, WSHost: "127.0.0.1", WSPort: 0, - WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, - HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, + WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "miner"}, + HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "miner"}, } gethInstance, err := createGethNode(false, nodeConfig, ethConfig, opts...) @@ -82,8 +83,8 @@ func defaultNodeConfig(name string, jwtPath string) *node.Config { AuthPort: 0, HTTPHost: "127.0.0.1", HTTPPort: 0, - WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, - HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, + WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "miner"}, + HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "miner"}, JWTSecret: jwtPath, } } @@ -104,6 +105,9 @@ func InitL2(name string, genesis *core.Genesis, jwtPath string, opts ...GethOpti // enough to build blocks within 1 second, but high enough to avoid unnecessary test CPU cycles. Recommit: time.Millisecond * 400, }, + TxPool: legacypool.Config{ + NoLocals: true, + }, } nodeConfig := defaultNodeConfig(fmt.Sprintf("l2-geth-%v", name), jwtPath) return createGethNode(true, nodeConfig, ethConfig, opts...) diff --git a/op-e2e/system/da/brotli_batcher_test.go b/op-e2e/system/da/brotli_batcher_test.go index a55e5bced8ad..fe9b4a9fab97 100644 --- a/op-e2e/system/da/brotli_batcher_test.go +++ b/op-e2e/system/da/brotli_batcher_test.go @@ -8,6 +8,7 @@ import ( "time" op_e2e "github.com/ethereum-optimism/optimism/op-e2e" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" @@ -67,7 +68,7 @@ func TestBrotliBatcherFjord(t *testing.T) { cfg.DeployConfig.L2GenesisFjordTimeOffset = &genesisActivation // set up batcher to use brotli - sys, err := cfg.Start(t, e2esys.StartOption{Key: "compressionAlgo", Role: "brotli", Action: nil}) + sys, err := cfg.Start(t, e2esys.WithBatcherCompressionAlgo(derive.Brotli)) require.Nil(t, err, "Error starting up system") log := testlog.Logger(t, log.LevelInfo) diff --git a/op-e2e/system/da/da_throttling_test.go b/op-e2e/system/da/da_throttling_test.go new file mode 100644 index 000000000000..e98ad9d3338d --- /dev/null +++ b/op-e2e/system/da/da_throttling_test.go @@ -0,0 +1,192 @@ +package da + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/node" + + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + op_e2e "github.com/ethereum-optimism/optimism/op-e2e" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-service/sources" +) + +const ( + bigTxSize = 10000 // amount of incompressible calldata to put in a "big" transaction +) + +func TestDATxThrottling(t *testing.T) { + op_e2e.InitParallel(t) + + cfg, rollupClient, l2Seq, l2Verif, batcher := setupTest(t, 100, 0) + + sendTx := func(senderKey *ecdsa.PrivateKey, nonce uint64, size int) *types.Receipt { + hash := sendTx(t, senderKey, nonce, size, cfg.L2ChainIDBig(), l2Seq) + return waitForReceipt(t, hash, l2Seq) + } + + // send a big transaction before throttling could have started, this transaction should land + receipt := sendTx(cfg.Secrets.Alice, 0, bigTxSize) + + // start batch submission, which should trigger throttling future large transactions + err := batcher.StartBatchSubmitting() + require.NoError(t, err) + + // wait until the block containing the above tx shows up as safe to confirm batcher is running. + waitForBlock(t, receipt.BlockNumber, l2Verif, rollupClient) + + // send another big tx, this one should get "stuck" so we wait for its receipt in a parallel goroutine. + done := make(chan bool, 1) + var bigReceipt *types.Receipt + go func() { + bigReceipt = sendTx(cfg.Secrets.Alice, 1, bigTxSize) + done <- true + }() + + safeBlockInclusionDuration := time.Duration(6*cfg.DeployConfig.L1BlockTime) * time.Second + time.Sleep(safeBlockInclusionDuration) + require.Nil(t, bigReceipt, "large tx did not get throttled") + + // Send a small tx, it should get included before the earlier one as long as it's from another sender + r := sendTx(cfg.Secrets.Bob, 0, 0) + // wait until the block the tx was first included in shows up in the safe chain on the verifier + waitForBlock(t, r.BlockNumber, l2Verif, rollupClient) + + // second tx should still be throttled + require.Nil(t, bigReceipt, "large tx did not get throttled") + + // disable throttling to let big tx through + batcher.Config.ThrottleTxSize = 0 + <-done + require.NotNil(t, bigReceipt, "large tx did not get throttled") +} + +func TestDABlockThrottling(t *testing.T) { + op_e2e.InitParallel(t) + cfg, rollupClient, l2Seq, l2Verif, batcher := setupTest(t, 0, bigTxSize+bigTxSize/10) + + sendTx := func(senderKey *ecdsa.PrivateKey, nonce uint64, size int) common.Hash { + return sendTx(t, senderKey, nonce, size, cfg.L2ChainIDBig(), l2Seq) + } + + // Send three big transactions before throttling could have started and make sure some eventually appear in the same + // block to confirm there is no block-level DA throttling active. This usually happens the first try but might + // require a second iteration in some cases due to stochasticity. + nonce := uint64(0) + for { + h1 := sendTx(cfg.Secrets.Alice, nonce, bigTxSize) + h2 := sendTx(cfg.Secrets.Bob, nonce, bigTxSize) + h3 := sendTx(cfg.Secrets.Mallory, nonce, bigTxSize) + nonce++ + + r1 := waitForReceipt(t, h1, l2Seq) + r2 := waitForReceipt(t, h2, l2Seq) + r3 := waitForReceipt(t, h3, l2Seq) + + // wait until the blocks containing the above txs show up in the unsafe chain + waitForBlock(t, r1.BlockNumber, l2Seq, rollupClient) + waitForBlock(t, r2.BlockNumber, l2Seq, rollupClient) + waitForBlock(t, r3.BlockNumber, l2Seq, rollupClient) + t.Log("Some block numbers should be the same:", r1.BlockNumber, r2.BlockNumber, r3.BlockNumber) + + if r1.BlockNumber.Cmp(r2.BlockNumber) == 0 || r1.BlockNumber.Cmp(r3.BlockNumber) == 0 || r2.BlockNumber.Cmp(r3.BlockNumber) == 0 { + // At least 2 transactions appeared in the same block, so we can exit the loop. + // But first we start batch submission, which will enabling DA throttling. + err := batcher.StartBatchSubmitting() + require.NoError(t, err) + // wait for a safe block containing one of the above transactions to ensure the batcher is running + waitForBlock(t, r1.BlockNumber, l2Verif, rollupClient) + break + } + t.Log("Another iteration required:", nonce) + } + + // Send 3 more big transactions at a time, but this time they must all appear in different blocks due to the + // block-level DA limit. Repeat the test 3 times to reduce the probability this happened just due to bad luck. + for i := 0; i < 3; i++ { + h1 := sendTx(cfg.Secrets.Alice, nonce, bigTxSize) + h2 := sendTx(cfg.Secrets.Bob, nonce, bigTxSize) + h3 := sendTx(cfg.Secrets.Mallory, nonce, bigTxSize) + nonce++ + + r1 := waitForReceipt(t, h1, l2Seq) + r2 := waitForReceipt(t, h2, l2Seq) + r3 := waitForReceipt(t, h3, l2Seq) + t.Log("Block numbers should all be different:", r1.BlockNumber, r2.BlockNumber, r3.BlockNumber) + + require.NotEqual(t, 0, r1.BlockNumber.Cmp(r2.BlockNumber)) + require.NotEqual(t, 0, r1.BlockNumber.Cmp(r3.BlockNumber)) + require.NotEqual(t, 0, r2.BlockNumber.Cmp(r3.BlockNumber)) + } +} + +func setupTest(t *testing.T, maxTxSize, maxBlockSize uint64) (e2esys.SystemConfig, *sources.RollupClient, *ethclient.Client, *ethclient.Client, *batcher.TestBatchSubmitter) { + cfg := e2esys.DefaultSystemConfig(t) + cfg.GethOptions["sequencer"] = append(cfg.GethOptions["sequencer"], []geth.GethOption{ + func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error { + ethCfg.Miner.GasCeil = 30_000_000 + return nil + }, + }...) + // disable batcher because we start it manually later + cfg.DisableBatcher = true + + sys, err := cfg.Start(t, + e2esys.WithBatcherThrottling(500*time.Millisecond, 1, maxTxSize, maxBlockSize)) + require.NoError(t, err, "Error starting up system") + + rollupClient := sys.RollupClient("verifier") + l2Seq := sys.NodeClient("sequencer") + l2Verif := sys.NodeClient("verifier") + batcher := sys.BatchSubmitter.ThrottlingTestDriver() + + return cfg, rollupClient, l2Seq, l2Verif, batcher +} + +// sendTx sends a tx containing the 'size' amount of random calldata +func sendTx(t *testing.T, senderKey *ecdsa.PrivateKey, nonce uint64, size int, chainID *big.Int, cl *ethclient.Client) common.Hash { + randomBytes := make([]byte, size) + _, err := rand.Read(randomBytes) + if err != nil { + panic(err) + } + tx := types.MustSignNewTx(senderKey, types.LatestSignerForChainID(chainID), &types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: &common.Address{0xff, 0xff}, + Value: big.NewInt(1_000_000_000), + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 21_000 + uint64(len(randomBytes))*16, + Data: randomBytes, + }) + err = cl.SendTransaction(context.Background(), tx) + require.NoError(t, err, "sending L2 tx") + return tx.Hash() +} + +func waitForReceipt(t *testing.T, hash common.Hash, cl *ethclient.Client) *types.Receipt { + receipt, err := wait.ForReceiptOK(context.Background(), cl, hash) + require.NoError(t, err, "waiting for L2 tx") + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "tx not successful") + return receipt +} + +func waitForBlock(t *testing.T, blockNumber *big.Int, cl *ethclient.Client, rc *sources.RollupClient) { + _, err := geth.WaitForBlock(blockNumber, cl) + require.NoError(t, err, "Waiting for block on verifier") + require.NoError(t, wait.ForProcessingFullBatch(context.Background(), rc)) +} diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index 5d046b3c649c..9ab6d2de14f6 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -471,6 +471,9 @@ type StartOption struct { Key string Role string Action SystemConfigHook + + // Batcher CLIConfig modifications to apply before starting the batcher. + BatcherMod func(*bss.CLIConfig) } type startOptions struct { @@ -491,6 +494,25 @@ func parseStartOptions(_opts []StartOption) (startOptions, error) { }, nil } +func WithBatcherCompressionAlgo(ca derive.CompressionAlgo) StartOption { + return StartOption{ + BatcherMod: func(cfg *bss.CLIConfig) { + cfg.CompressionAlgo = ca + }, + } +} + +func WithBatcherThrottling(interval time.Duration, threshold, txSize, blockSize uint64) StartOption { + return StartOption{ + BatcherMod: func(cfg *bss.CLIConfig) { + cfg.ThrottleInterval = interval + cfg.ThrottleThreshold = threshold + cfg.ThrottleTxSize = txSize + cfg.ThrottleBlockSize = blockSize + }, + } +} + func (s *startOptions) Get(key, role string) (SystemConfigHook, bool) { v, ok := s.opts[key+":"+role] return v, ok @@ -859,12 +881,6 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, batcherTargetNumFrames = 1 } - var compressionAlgo derive.CompressionAlgo = derive.Zlib - // if opt has brotli key, set the compression algo as brotli - if _, ok := parsedStartOpts.Get("compressionAlgo", "brotli"); ok { - compressionAlgo = derive.Brotli10 - } - var batcherAltDACLIConfig altda.CLIConfig if cfg.DeployConfig.UseAltDA { fakeAltDAServer := altda.NewFakeDAServer("127.0.0.1", 0, sys.Cfg.Loggers["da-server"]) @@ -902,9 +918,17 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, BatchType: cfg.BatcherBatchType, MaxBlocksPerSpanBatch: cfg.BatcherMaxBlocksPerSpanBatch, DataAvailabilityType: sys.Cfg.DataAvailabilityType, - CompressionAlgo: compressionAlgo, + CompressionAlgo: derive.Zlib, AltDA: batcherAltDACLIConfig, } + + // Apply batcher cli modifications + for _, opt := range startOpts { + if opt.BatcherMod != nil { + opt.BatcherMod(batcherCLIConfig) + } + } + // Batch Submitter batcher, err := bss.BatcherServiceFromCLIConfig(context.Background(), "0.0.1", batcherCLIConfig, sys.Cfg.Loggers["batcher"]) if err != nil { diff --git a/op-service/dial/ethclient_interface.go b/op-service/dial/ethclient_interface.go index 58a2070974ee..292c7b07fa4c 100644 --- a/op-service/dial/ethclient_interface.go +++ b/op-service/dial/ethclient_interface.go @@ -5,12 +5,14 @@ import ( "math/big" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" ) // EthClientInterface is an interface for providing an ethclient.Client // It does not describe all of the functions an ethclient.Client has, only the ones used by callers of the L2 Providers type EthClientInterface interface { BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) + Client() *rpc.Client Close() } diff --git a/op-service/metrics/factory.go b/op-service/metrics/factory.go index 0bd4da2373db..315b7c52e058 100644 --- a/op-service/metrics/factory.go +++ b/op-service/metrics/factory.go @@ -9,6 +9,7 @@ type Factory interface { NewCounter(opts prometheus.CounterOpts) prometheus.Counter NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge + NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec @@ -63,6 +64,15 @@ func (d *documentor) NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge { return d.factory.NewGauge(opts) } +func (d *documentor) NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc { + d.metrics = append(d.metrics, DocumentedMetric{ + Type: "gauge", + Name: fullName(opts.Namespace, opts.Subsystem, opts.Name), + Help: opts.Help, + }) + return d.factory.NewGaugeFunc(opts, function) +} + func (d *documentor) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { d.metrics = append(d.metrics, DocumentedMetric{ Type: "gauge", diff --git a/op-service/testutils/mock_eth_client.go b/op-service/testutils/mock_eth_client.go index fcb0a60c24b0..2d82966724b3 100644 --- a/op-service/testutils/mock_eth_client.go +++ b/op-service/testutils/mock_eth_client.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -16,6 +17,11 @@ type MockEthClient struct { mock.Mock } +func (m *MockEthClient) Client() *rpc.Client { + out := m.Mock.Called() + return out.Get(0).(*rpc.Client) +} + func (m *MockEthClient) InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error) { out := m.Mock.Called(hash) return *out.Get(0).(*eth.BlockInfo), out.Error(1) diff --git a/ops-bedrock/l2-op-geth-entrypoint.sh b/ops-bedrock/l2-op-geth-entrypoint.sh index f760a87f1875..eee8684e97b0 100644 --- a/ops-bedrock/l2-op-geth-entrypoint.sh +++ b/ops-bedrock/l2-op-geth-entrypoint.sh @@ -31,12 +31,12 @@ exec geth \ --http.vhosts="*" \ --http.addr=0.0.0.0 \ --http.port="$RPC_PORT" \ - --http.api=web3,debug,eth,txpool,net,engine \ + --http.api=web3,debug,eth,txpool,net,engine,miner \ --ws \ --ws.addr=0.0.0.0 \ --ws.port="$WS_PORT" \ --ws.origins="*" \ - --ws.api=debug,eth,txpool,net,engine \ + --ws.api=debug,eth,txpool,net,engine,miner \ --syncmode=full \ --nodiscover \ --maxpeers=0 \ From bc9b6cd588588c9c4167c926a1782c658e5df921 Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Wed, 6 Nov 2024 11:28:27 +0100 Subject: [PATCH 138/451] op-batcher: Enable throttling by default, shutdown on broken RPC (#12840) --- op-batcher/batcher/driver.go | 37 ++++++++++++++++++++++--------- op-batcher/batcher/driver_test.go | 2 -- op-batcher/flags/flags.go | 6 ++--- op-service/eth/types.go | 5 +++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 000fb89d16f1..6d8b6eb9da1f 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -10,6 +10,16 @@ import ( "sync" "time" + "golang.org/x/sync/errgroup" + + "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/txpool" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" + altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -17,13 +27,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/txmgr" - "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/txpool" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" - "golang.org/x/sync/errgroup" ) var ( @@ -543,14 +546,26 @@ func (l *BatchSubmitter) throttlingLoop(throttlingLoopDone chan struct{}) { maxBlockSize = l.Config.ThrottleBlockSize } } - var success bool + var ( + success bool + rpcErr rpc.Error + ) if err := cl.Client().CallContext( - ctx, &success, SetMaxDASizeMethod, hexutil.Uint64(maxTxSize), hexutil.Uint64(maxBlockSize)); err != nil { - l.Log.Error("SetMaxDASize rpc failed", "err", err) + ctx, &success, SetMaxDASizeMethod, hexutil.Uint64(maxTxSize), hexutil.Uint64(maxBlockSize), + ); errors.As(err, &rpcErr) && eth.ErrorCode(rpcErr.ErrorCode()).IsGenericRPCError() { + l.Log.Error("SetMaxDASize rpc unavailable or broken, shutting down. Either enable it or disable throttling.", "err", err) + // We'd probably hit this error right after startup, so a short shutdown duration should suffice. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + // Always returns nil. An error is only returned to expose this function as an RPC. + _ = l.StopBatchSubmitting(ctx) + return + } else if err != nil { + l.Log.Error("SetMaxDASize rpc failed, retrying.", "err", err) return } if !success { - l.Log.Error("Result of SetMaxDASize was false") + l.Log.Error("Result of SetMaxDASize was false, retrying.") } } diff --git a/op-batcher/batcher/driver_test.go b/op-batcher/batcher/driver_test.go index 5ce0983bfe1a..f4e39e22cee2 100644 --- a/op-batcher/batcher/driver_test.go +++ b/op-batcher/batcher/driver_test.go @@ -86,7 +86,6 @@ func TestBatchSubmitter_SafeL1Origin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.failsToFetchSyncStatus { ep.rollupClient.ExpectSyncStatus(ð.SyncStatus{}, errors.New("failed to fetch sync status")) - } else { ep.rollupClient.ExpectSyncStatus(ð.SyncStatus{ SafeL2: eth.L2BlockRef{ @@ -107,7 +106,6 @@ func TestBatchSubmitter_SafeL1Origin(t *testing.T) { } }) } - } func TestBatchSubmitter_SafeL1Origin_FailsToResolveRollupClient(t *testing.T) { diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 538b70b6c9fa..d5681ea87234 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -157,9 +157,9 @@ var ( EnvVars: prefixEnvVars("WAIT_NODE_SYNC"), } ThrottleIntervalFlag = &cli.DurationFlag{ - Name: "throttle-interval", - Usage: "Interval between potential DA throttling actions. Zero (default) disables throttling. " + - "Recommended to be set to L2 block time if enabling.", + Name: "throttle-interval", + Usage: "Interval between potential DA throttling actions. Zero disables throttling.", + Value: 2 * time.Second, EnvVars: prefixEnvVars("THROTTLE_INTERVAL"), } ThrottleThresholdFlag = &cli.IntFlag{ diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 7b4d1c053348..122e9a4df783 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -25,9 +25,14 @@ func (c ErrorCode) IsEngineError() bool { return -38100 < c && c <= -38000 } +func (c ErrorCode) IsGenericRPCError() bool { + return -32700 < c && c <= -32600 +} + // Engine error codes used to be -3200x, but were rebased to -3800x: // https://github.com/ethereum/execution-apis/pull/214 const ( + MethodNotFound ErrorCode = -32601 // RPC method not found or not available. InvalidParams ErrorCode = -32602 UnknownPayload ErrorCode = -38001 // Payload does not exist / is not available. InvalidForkchoiceState ErrorCode = -38002 // Forkchoice state is invalid / inconsistent. From ac5587eb15703d9cff9b365159fc2c8d5c6bb4ec Mon Sep 17 00:00:00 2001 From: Francis Li Date: Wed, 6 Nov 2024 11:43:36 -0800 Subject: [PATCH 139/451] docs: add conductor runbook (#12604) --- op-conductor/README.md | 2 ++ op-conductor/RUNBOOK.md | 78 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 op-conductor/RUNBOOK.md diff --git a/op-conductor/README.md b/op-conductor/README.md index 084899e2fe4b..497156b8eee8 100644 --- a/op-conductor/README.md +++ b/op-conductor/README.md @@ -13,6 +13,8 @@ The design will provide below guarantees: 2. No unsafe head stall during network partition 3. 100% uptime with no more than 1 node failure (for a standard 3 node setup) +For configuration and runbook, please refer to [RUNBOOK.md](./RUNBOOK.md) + ## Design ### Architecture diff --git a/op-conductor/RUNBOOK.md b/op-conductor/RUNBOOK.md new file mode 100644 index 000000000000..8e80fde6b6c0 --- /dev/null +++ b/op-conductor/RUNBOOK.md @@ -0,0 +1,78 @@ +## op-conductor runbook + +### conductor configurations + +In order to setup op-conductor, you need to configure the following env vars for both op-conductor and op-node service: + +#### op-node + +```env +OP_NODE_CONDUCTOR_ENABLED=true +OP_NODE_CONDUCTOR_RPC= # for example http://conductor:8545 +``` + +#### op-conductor + +```env +# prefix for the server id, used to identify the server in the raft cluster +RAFT_SERVER_ID_PREFIX= # for example, sequencer-1, sequencer-2, etc +OP_CONDUCTOR_RAFT_STORAGE_DIR= +OP_CONDUCTOR_RPC_ADDR= # for example, 0.0.0.0 +OP_CONDUCTOR_RPC_PORT= # for example, 8545 +OP_CONDUCTOR_METRICS_ENABLED=true/false +OP_CONDUCTOR_METRICS_ADDR= # for example 0.0.0.0 +OP_CONDUCTOR_METRICS_PORT= # for example 7300 +OP_CONDUCTOR_CONSENSUS_PORT= # for example 50050 +OP_CONDUCTOR_PAUSED=true # set to true to start conductor in paused state +OP_CONDUCTOR_NODE_RPC= # for example, http://op-node:8545 +OP_CONDUCTOR_EXECUTION_RPC= # for example, http://op-geth:8545 +OP_CONDUCTOR_NETWORK= # for example, base-mainnet, op-mainnet, etc, should be same as OP_NODE_NETWORK +OP_CONDUCTOR_HEALTHCHECK_INTERVAL= # in seconds +OP_CONDUCTOR_HEALTHCHECK_UNSAFE_INTERVAL= # Interval allowed between unsafe head and now measured in seconds in seconds +OP_CONDUCTOR_HEALTHCHECK_MIN_PEER_COUNT= # minimum number of peers required to be considered healthy +OP_CONDUCTOR_RAFT_BOOTSTRAP=true/false # set to true if you want to bootstrap the raft cluster +``` + +### How to bootstrap a sequencer cluster from scratch + +In normal situations, you probably have a running sequencer already and you want to turn it into a HA cluster. What you need to do in this situation is to: + +1. start a completely new sequencer with above mentioned configurations and + 1. `OP_CONDUCTOR_RAFT_BOOTSTRAP=true` set on op-conductor + 2. `OP_CONDUCTOR_PAUSED=true` set on op-conductor + 3. `OP_NODE_SEQUENCER_ENABLED=true` set on op-node +2. wait for the new sequencer to start and get synced up with the rest of the nodes +3. once the new sequencer is synced up, manually or use automation to stop sequencing on the old sequencer and start sequencing on the new sequencer +4. resume the conductor on the new sequencer by calling `conductor_resume` json rpc method on op-conductor +5. set `OP_CONDUCTOR_RAFT_BOOTSTRAP=false` on the sequencer so that it doesn't attempt to bootstrap the cluster during redeploy + +Now you have a single HA sequencer which treats itself as the cluster leader! Next steps would be to add more sequencers to the cluster depending on your needs. For example, we want a 3-node cluster, you can follow the same process to add 2 more sequencers. + +1. start a new sequencer with + 1. `OP_CONDUCTOR_RAFT_BOOTSTRAP=false` set on op-conductor + 2. `OP_CONDUCTOR_PAUSED=true` set on op-conductor +2. wait for the new sequencer to start and get synced up with the rest of the nodes +3. once the new sequencer is synced up, manually or use automation to add it to the cluster by calling `conductor_addServerAsVoter` json rpc method on the leader sequencer +4. call `conductor_clusterMembership` json rpc method on the leader sequencer to get the updated cluster membership +5. resume the conductor on the new sequencer by calling `conductor_resume` json rpc method on op-conductor + +Once finished, you should have a 3-node HA sequencer cluster! + +### Redeploy a HA sequencer + +For every redeploy, depending on your underlying infrastructure, you need to make sure to: + +1. `OP_CONDUCTOR_PAUSED=true` set on op-conductor so that conductor doesn't attempt to control sequencer while it's still syncing / redeploying +2. make sure sequencer is caught up with the rest of the nodes (this step isn't strictly necessary as conductor could handle this, but from a HA perspective, it does not make sense to have a sequencer that is lagging behind to join the cluster to potentially become the leader) +3. resume conductor after it's caught up with the rest of the nodes so that conductor can start managing the sequencer + +### Disaster recovery + +Whenever there are a disaster situation that you see no route to have 2 healthy conductor in the cluster communicating with each other, you need to manually intervene to resume sequencing. The steps are as follows: + +1. call `conductor_pause` json rpc method on the all conductors so that they don't attempt to start / stop sequencer +2. choose a sequencer that can be used to resume sequencing +3. call `conductor_overrideLeader` json rpc method on the conductor to force it to treat itself as the leader +4. If no conductor is functioning, call `admin_overrideLeader` json rpc method on the op-node to force it to treat itself as the leader +5. manually start sequencing on the chosen sequencer +6. Go back to bootstrap step to re-bootstrap the cluster. From dd10e2e299cf9405b856ca4101629344c9d011f8 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 6 Nov 2024 11:51:35 -0800 Subject: [PATCH 140/451] vm-runner: Check L1 head sync status (#12853) --- op-challenger/runner/runner.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-challenger/runner/runner.go b/op-challenger/runner/runner.go index 289bb0cfe105..312981430413 100644 --- a/op-challenger/runner/runner.go +++ b/op-challenger/runner/runner.go @@ -199,6 +199,9 @@ func (r *Runner) createGameInputs(ctx context.Context, client *sources.RollupCli // This only matters if op-node is behind and hasn't processed all finalized L1 blocks yet. l1Head = status.CurrentL1 } + if l1Head.Number == 0 { + return utils.LocalGameInputs{}, errors.New("l1 head is 0") + } blockNumber, err := r.findL2BlockNumberToDispute(ctx, client, l1Head.Number, status.FinalizedL2.Number) if err != nil { return utils.LocalGameInputs{}, fmt.Errorf("failed to find l2 block number to dispute: %w", err) From 11843b48eed9beefe2111c5bde817a971ca8bc89 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Thu, 7 Nov 2024 07:39:24 +1000 Subject: [PATCH 141/451] op-deployer: Add deploy mips script (#12835) * op-deployer: Add deploy mips script * op-deployer: Explicitly set the PreimageOracle to use with deployed MIPS * op-deployer: Validate mips version in go code. --- .../pkg/deployer/bootstrap/dispute_game.go | 2 +- op-deployer/pkg/deployer/bootstrap/flags.go | 21 ++ op-deployer/pkg/deployer/bootstrap/mips.go | 196 ++++++++++++++++++ op-deployer/pkg/deployer/opcm/dispute_game.go | 2 +- op-deployer/pkg/deployer/opcm/mips.go | 65 ++++++ op-deployer/pkg/deployer/opcm/mips_test.go | 36 ++++ .../scripts/deploy/DeployMIPS.s.sol | 109 ++++++++++ 7 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 op-deployer/pkg/deployer/bootstrap/mips.go create mode 100644 op-deployer/pkg/deployer/opcm/mips.go create mode 100644 op-deployer/pkg/deployer/opcm/mips_test.go create mode 100644 packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol diff --git a/op-deployer/pkg/deployer/bootstrap/dispute_game.go b/op-deployer/pkg/deployer/bootstrap/dispute_game.go index 65ca4a731124..2b17a35be7bb 100644 --- a/op-deployer/pkg/deployer/bootstrap/dispute_game.go +++ b/op-deployer/pkg/deployer/bootstrap/dispute_game.go @@ -38,7 +38,7 @@ type DisputeGameConfig struct { MinProposalSizeBytes uint64 ChallengePeriodSeconds uint64 - MipsVersion uint8 + MipsVersion uint64 GameKind string GameType uint32 AbsolutePrestate common.Hash diff --git a/op-deployer/pkg/deployer/bootstrap/flags.go b/op-deployer/pkg/deployer/bootstrap/flags.go index a7b80daf6e7f..73f99e7b4325 100644 --- a/op-deployer/pkg/deployer/bootstrap/flags.go +++ b/op-deployer/pkg/deployer/bootstrap/flags.go @@ -28,6 +28,7 @@ const ( L2ChainIdFlagName = "l2-chain-id" ProposerFlagName = "proposer" ChallengerFlagName = "challenger" + PreimageOracleFlagName = "preimage-oracle" ) var ( @@ -140,6 +141,12 @@ var ( EnvVars: deployer.PrefixEnvVar("CHALLENGER"), Value: common.Address{}.Hex(), } + PreimageOracleFlag = &cli.StringFlag{ + Name: PreimageOracleFlagName, + Usage: "Preimage oracle address.", + EnvVars: deployer.PrefixEnvVar("PREIMAGE_ORACLE"), + Value: common.Address{}.Hex(), + } ) var OPCMFlags = []cli.Flag{ @@ -181,6 +188,14 @@ var DisputeGameFlags = []cli.Flag{ ChallengerFlag, } +var MIPSFlags = []cli.Flag{ + deployer.L1RPCURLFlag, + deployer.PrivateKeyFlag, + ArtifactsLocatorFlag, + PreimageOracleFlag, + MIPSVersionFlag, +} + var Commands = []*cli.Command{ { Name: "opcm", @@ -200,4 +215,10 @@ var Commands = []*cli.Command{ Flags: cliapp.ProtectFlags(DisputeGameFlags), Action: DisputeGameCLI, }, + { + Name: "mips", + Usage: "Bootstrap an instance of MIPS.", + Flags: cliapp.ProtectFlags(MIPSFlags), + Action: MIPSCLI, + }, } diff --git a/op-deployer/pkg/deployer/bootstrap/mips.go b/op-deployer/pkg/deployer/bootstrap/mips.go new file mode 100644 index 000000000000..42abae208eb5 --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/mips.go @@ -0,0 +1,196 @@ +package bootstrap + +import ( + "context" + "crypto/ecdsa" + "fmt" + "strings" + + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" +) + +type MIPSConfig struct { + L1RPCUrl string + PrivateKey string + Logger log.Logger + ArtifactsLocator *artifacts2.Locator + + privateKeyECDSA *ecdsa.PrivateKey + + PreimageOracle common.Address + MipsVersion uint64 +} + +func (c *MIPSConfig) Check() error { + if c.L1RPCUrl == "" { + return fmt.Errorf("l1RPCUrl must be specified") + } + + if c.PrivateKey == "" { + return fmt.Errorf("private key must be specified") + } + + privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x")) + if err != nil { + return fmt.Errorf("failed to parse private key: %w", err) + } + c.privateKeyECDSA = privECDSA + + if c.Logger == nil { + return fmt.Errorf("logger must be specified") + } + + if c.ArtifactsLocator == nil { + return fmt.Errorf("artifacts locator must be specified") + } + + if c.PreimageOracle == (common.Address{}) { + return fmt.Errorf("preimage oracle must be specified") + } + + if c.MipsVersion == 0 { + return fmt.Errorf("mips version must be specified") + } + if c.MipsVersion != 1 && c.MipsVersion != 2 { + return fmt.Errorf("mips version must be either 1 or 2") + } + + return nil +} + +func MIPSCLI(cliCtx *cli.Context) error { + logCfg := oplog.ReadCLIConfig(cliCtx) + l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) + oplog.SetGlobalLogHandler(l.Handler()) + + l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) + privateKey := cliCtx.String(deployer.PrivateKeyFlagName) + artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) + artifactsLocator := new(artifacts2.Locator) + if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { + return fmt.Errorf("failed to parse artifacts URL: %w", err) + } + + mipsVersion := cliCtx.Uint64(MIPSVersionFlagName) + preimageOracle := common.HexToAddress(cliCtx.String(PreimageOracleFlagName)) + + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + + return MIPS(ctx, MIPSConfig{ + L1RPCUrl: l1RPCUrl, + PrivateKey: privateKey, + Logger: l, + ArtifactsLocator: artifactsLocator, + MipsVersion: mipsVersion, + PreimageOracle: preimageOracle, + }) +} + +func MIPS(ctx context.Context, cfg MIPSConfig) error { + if err := cfg.Check(); err != nil { + return fmt.Errorf("invalid config for MIPS: %w", err) + } + + lgr := cfg.Logger + progressor := func(curr, total int64) { + lgr.Info("artifacts download progress", "current", curr, "total", total) + } + + artifactsFS, cleanup, err := artifacts2.Download(ctx, cfg.ArtifactsLocator, progressor) + if err != nil { + return fmt.Errorf("failed to download artifacts: %w", err) + } + defer func() { + if err := cleanup(); err != nil { + lgr.Warn("failed to clean up artifacts", "err", err) + } + }() + + l1Client, err := ethclient.Dial(cfg.L1RPCUrl) + if err != nil { + return fmt.Errorf("failed to connect to L1 RPC: %w", err) + } + + chainID, err := l1Client.ChainID(ctx) + if err != nil { + return fmt.Errorf("failed to get chain ID: %w", err) + } + + signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) + chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) + + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: lgr, + ChainID: chainID, + Client: l1Client, + Signer: signer, + From: chainDeployer, + }) + if err != nil { + return fmt.Errorf("failed to create broadcaster: %w", err) + } + + nonce, err := l1Client.NonceAt(ctx, chainDeployer, nil) + if err != nil { + return fmt.Errorf("failed to get starting nonce: %w", err) + } + + host, err := env.DefaultScriptHost( + bcaster, + lgr, + chainDeployer, + artifactsFS, + nonce, + ) + if err != nil { + return fmt.Errorf("failed to create script host: %w", err) + } + + var release string + if cfg.ArtifactsLocator.IsTag() { + release = cfg.ArtifactsLocator.Tag + } else { + release = "dev" + } + + lgr.Info("deploying dispute game", "release", release) + + dgo, err := opcm.DeployMIPS( + host, + opcm.DeployMIPSInput{ + MipsVersion: cfg.MipsVersion, + PreimageOracle: cfg.PreimageOracle, + }, + ) + if err != nil { + return fmt.Errorf("error deploying dispute game: %w", err) + } + + if _, err := bcaster.Broadcast(ctx); err != nil { + return fmt.Errorf("failed to broadcast: %w", err) + } + + lgr.Info("deployed dispute game") + + if err := jsonutil.WriteJSON(dgo, ioutil.ToStdOut()); err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + return nil +} diff --git a/op-deployer/pkg/deployer/opcm/dispute_game.go b/op-deployer/pkg/deployer/opcm/dispute_game.go index 001987afaeed..0e117c040d83 100644 --- a/op-deployer/pkg/deployer/opcm/dispute_game.go +++ b/op-deployer/pkg/deployer/opcm/dispute_game.go @@ -11,7 +11,7 @@ import ( type DeployDisputeGameInput struct { Release string StandardVersionsToml string - MipsVersion uint8 + MipsVersion uint64 MinProposalSizeBytes uint64 ChallengePeriodSeconds uint64 GameKind string diff --git a/op-deployer/pkg/deployer/opcm/mips.go b/op-deployer/pkg/deployer/opcm/mips.go new file mode 100644 index 000000000000..5d1a7798cedb --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/mips.go @@ -0,0 +1,65 @@ +package opcm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +type DeployMIPSInput struct { + MipsVersion uint64 + PreimageOracle common.Address +} + +func (input *DeployMIPSInput) InputSet() bool { + return true +} + +type DeployMIPSOutput struct { + MipsSingleton common.Address +} + +func (output *DeployMIPSOutput) CheckOutput(input common.Address) error { + return nil +} + +type DeployMIPSScript struct { + Run func(input, output common.Address) error +} + +func DeployMIPS( + host *script.Host, + input DeployMIPSInput, +) (DeployMIPSOutput, error) { + var output DeployMIPSOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress[*DeployMIPSInput](host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployMIPSInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployMIPSOutput](host, outputAddr, &output, + script.WithFieldSetter[*DeployMIPSOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployMIPSOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployMIPS" + deployScript, cleanupDeploy, err := script.WithScript[DeployMIPSScript](host, "DeployMIPS.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/opcm/mips_test.go b/op-deployer/pkg/deployer/opcm/mips_test.go new file mode 100644 index 000000000000..04f8d93ae9bd --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/mips_test.go @@ -0,0 +1,36 @@ +package opcm + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestDeployMIPS(t *testing.T) { + _, artifacts := testutil.LocalArtifacts(t) + + host, err := env.DefaultScriptHost( + broadcaster.NoopBroadcaster(), + testlog.Logger(t, log.LevelInfo), + common.Address{'D'}, + artifacts, + 0, + ) + require.NoError(t, err) + + input := DeployMIPSInput{ + MipsVersion: 1, + PreimageOracle: common.Address{0xab}, + } + + output, err := DeployMIPS(host, input) + require.NoError(t, err) + + require.NotEmpty(t, output.MipsSingleton) +} diff --git a/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol new file mode 100644 index 000000000000..5890630c9c22 --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { Script } from "forge-std/Script.sol"; + +// Scripts +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Interfaces +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; +import { IMIPS } from "src/cannon/interfaces/IMIPS.sol"; + +/// @title DeployMIPSInput +contract DeployMIPSInput is BaseDeployIO { + // Specify the PreimageOracle to use + address internal _preimageOracle; + + // Specify which MIPS version to use. + uint256 internal _mipsVersion; + + function set(bytes4 _sel, uint256 _value) public { + if (_sel == this.mipsVersion.selector) { + require(_value == 1 || _value == 2, "DeployMIPS: unknown mips version"); + _mipsVersion = _value; + } else { + revert("DeployMIPS: unknown selector"); + } + } + + function set(bytes4 _sel, address _value) public { + if (_sel == this.preimageOracle.selector) { + require(_value != address(0), "DeployMIPS: preimageOracle cannot be empty"); + _preimageOracle = _value; + } else { + revert("DeployMIPS: unknown selector"); + } + } + + function mipsVersion() public view returns (uint256) { + require(_mipsVersion != 0, "DeployMIPS: mipsVersion not set"); + require(_mipsVersion == 1 || _mipsVersion == 2, "DeployMIPS: unknown mips version"); + return _mipsVersion; + } + + function preimageOracle() public view returns (address) { + require(_preimageOracle != address(0), "DeployMIPS: preimageOracle not set"); + return _preimageOracle; + } +} + +/// @title DeployMIPSOutput +contract DeployMIPSOutput is BaseDeployIO { + IMIPS internal _mipsSingleton; + + function set(bytes4 _sel, address _value) public { + if (_sel == this.mipsSingleton.selector) { + require(_value != address(0), "DeployMIPS: mipsSingleton cannot be zero address"); + _mipsSingleton = IMIPS(_value); + } else { + revert("DeployMIPS: unknown selector"); + } + } + + function checkOutput(DeployMIPSInput _mi) public view { + DeployUtils.assertValidContractAddress(address(_mipsSingleton)); + assertValidDeploy(_mi); + } + + function mipsSingleton() public view returns (IMIPS) { + DeployUtils.assertValidContractAddress(address(_mipsSingleton)); + return _mipsSingleton; + } + + function assertValidDeploy(DeployMIPSInput _mi) public view { + assertValidMipsSingleton(_mi); + } + + function assertValidMipsSingleton(DeployMIPSInput _mi) internal view { + IMIPS mips = mipsSingleton(); + + require(address(mips.oracle()) == address(_mi.preimageOracle()), "MIPS-10"); + } +} + +/// @title DeployMIPS +contract DeployMIPS is Script { + function run(DeployMIPSInput _mi, DeployMIPSOutput _mo) public { + deployMipsSingleton(_mi, _mo); + _mo.checkOutput(_mi); + } + + function deployMipsSingleton(DeployMIPSInput _mi, DeployMIPSOutput _mo) internal { + IMIPS singleton; + uint256 mipsVersion = _mi.mipsVersion(); + IPreimageOracle preimageOracle = IPreimageOracle(_mi.preimageOracle()); + vm.broadcast(msg.sender); + singleton = IMIPS( + DeployUtils.create1({ + _name: mipsVersion == 1 ? "MIPS" : "MIPS2", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) + }) + ); + + vm.label(address(singleton), "MIPSSingleton"); + _mo.set(_mo.mipsSingleton.selector, address(singleton)); + } +} From b5a2955a1bbca510402d5e40ecf88d0cd06ebfbd Mon Sep 17 00:00:00 2001 From: Yann Hodique Date: Wed, 6 Nov 2024 22:42:04 +0100 Subject: [PATCH 142/451] goreleaser: cleanup (#12856) * chore(op-deployer): use goreleaser-pro schema This avoids getting schema validation errors wherever pro features are used. * chore: bump goreleaser-pro to 2.4.3 No functional change, just good hygiene. --- .circleci/config.yml | 2 +- op-deployer/.goreleaser.yaml | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 01eba4e466d6..061825f164a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1257,7 +1257,7 @@ jobs: command: | mkdir -p /tmp/goreleaser cd /tmp/goreleaser - curl -L -o goreleaser.tgz https://github.com/goreleaser/goreleaser-pro/releases/download/v2.3.2-pro/goreleaser-pro_Linux_x86_64.tar.gz + curl -L -o goreleaser.tgz https://github.com/goreleaser/goreleaser-pro/releases/download/v2.4.3-pro/goreleaser-pro_Linux_x86_64.tar.gz tar -xzvf goreleaser.tgz mv goreleaser /usr/local/bin/goreleaser - run: diff --git a/op-deployer/.goreleaser.yaml b/op-deployer/.goreleaser.yaml index 0104574ec865..5040ed4bc4d4 100644 --- a/op-deployer/.goreleaser.yaml +++ b/op-deployer/.goreleaser.yaml @@ -1,9 +1,4 @@ -# This is an example .goreleaser.yml file with some sensible defaults. -# Make sure to check the documentation at https://goreleaser.com - -# The lines below are called `modelines`. See `:help modeline` -# Feel free to remove those if you don't want/need to use them. -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json # vim: set ts=2 sw=2 tw=0 fo=cnqoj version: 2 From 15912abe12928473838284ebd2dcf45de0f23cb9 Mon Sep 17 00:00:00 2001 From: Yann Hodique Date: Wed, 6 Nov 2024 22:42:44 +0100 Subject: [PATCH 143/451] ci(lint): fix the lint-shellcheck target (#12857) Excessive backslashing made the command invalid. Also address the lint-shellcheck current discoveries. --- justfile | 2 +- .../contracts-bedrock/test/kontrol/scripts/run-kontrol.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/justfile b/justfile index 57d7fa5da218..788f96fc7cc0 100644 --- a/justfile +++ b/justfile @@ -10,7 +10,7 @@ semgrep-test: semgrep scan --test semgrep/ lint-shellcheck: - find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './packages/contracts-bedrock/kout*/*' -exec sh -c 'echo \"Checking $1\"; shellcheck \"$1\"' _ {} \\; + find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './packages/contracts-bedrock/kout*/*' -exec sh -c 'echo "Checking $1"; shellcheck "$1"' _ {} \; install-foundry: curl -L https://foundry.paradigm.xyz | bash && just update-foundry diff --git a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh index 935f6e3b4903..a1487eecd867 100755 --- a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh +++ b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh @@ -207,13 +207,13 @@ get_log_results # Now you can use ${results[0]} and ${results[1]} # to check the results of kontrol_build and kontrol_prove, respectively -if [ ${results[0]} -ne 0 ] && [ ${results[1]} -ne 0 ]; then +if [ "${results[0]}" -ne 0 ] && [ "${results[1]}" -ne 0 ]; then echo "Kontrol Build and Prove Failed" exit 1 -elif [ ${results[0]} -ne 0 ]; then +elif [ "${results[0]}" -ne 0 ]; then echo "Kontrol Build Failed" exit 1 -elif [ ${results[1]} -ne 0 ]; then +elif [ "${results[1]}" -ne 0 ]; then echo "Kontrol Prove Failed" exit 2 else From b6bd58eeea5e4b90d58defea19114d3b36071d1d Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Thu, 7 Nov 2024 04:43:39 +0700 Subject: [PATCH 144/451] maint: move semgrep folder again (#12828) Moves the semgrep folder back into .semgrep now that we worked out how to actually execute the tests when they're located inside of a hidden folder. --- .circleci/config.yml | 4 ++-- {semgrep => .semgrep/rules}/sol-rules.yaml | 0 {semgrep => .semgrep/tests}/sol-rules.t.sol | 0 .semgrepignore | 2 +- justfile | 4 ++-- packages/contracts-bedrock/justfile | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename {semgrep => .semgrep/rules}/sol-rules.yaml (100%) rename {semgrep => .semgrep/tests}/sol-rules.t.sol (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 061825f164a1..680577e5acad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1314,10 +1314,10 @@ workflows: - contracts-bedrock-build - semgrep-scan: name: semgrep-scan-local - scan_command: semgrep scan --timeout=100 --config=./semgrep --error . + scan_command: semgrep scan --timeout=100 --config .semgrep/rules/ --error . - semgrep-scan: name: semgrep-test - scan_command: semgrep scan --test semgrep/ + scan_command: semgrep scan --test --config .semgrep/rules/ .semgrep/tests/ - go-lint - fuzz-golang: name: fuzz-golang-<> diff --git a/semgrep/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml similarity index 100% rename from semgrep/sol-rules.yaml rename to .semgrep/rules/sol-rules.yaml diff --git a/semgrep/sol-rules.t.sol b/.semgrep/tests/sol-rules.t.sol similarity index 100% rename from semgrep/sol-rules.t.sol rename to .semgrep/tests/sol-rules.t.sol diff --git a/.semgrepignore b/.semgrepignore index 0e7e4044b758..b5fcb0303882 100644 --- a/.semgrepignore +++ b/.semgrepignore @@ -9,7 +9,7 @@ vendor/ *.min.js # Semgrep rules folder -semgrep/ +.semgrep/ # Semgrep-action log folder .semgrep_logs/ diff --git a/justfile b/justfile index 788f96fc7cc0..00a272e899ae 100644 --- a/justfile +++ b/justfile @@ -3,11 +3,11 @@ issues: # Runs semgrep on the entire monorepo. semgrep: - semgrep scan --config=semgrep --error . + semgrep scan --config .semgrep/rules/ --error . # Runs semgrep tests. semgrep-test: - semgrep scan --test semgrep/ + semgrep scan --test --config .semgrep/rules/ .semgrep/tests/ lint-shellcheck: find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './packages/contracts-bedrock/kout*/*' -exec sh -c 'echo "Checking $1"; shellcheck "$1"' _ {} \; diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index e73673634745..41cfd24bb2d6 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -163,7 +163,7 @@ semver-diff-check: build semver-diff-check-no-build # Checks that the semgrep tests are valid. semgrep-test-validity-check: - forge fmt ../../semgrep/sol-rules.t.sol --check + forge fmt ../../.semgrep/tests/sol-rules.t.sol --check # Checks that forge test names are correctly formatted. lint-forge-tests-check: @@ -199,11 +199,11 @@ check-kontrol-summaries-unchanged: # Runs semgrep on the contracts. semgrep: - cd ../../ && semgrep scan --config=semgrep ./packages/contracts-bedrock + cd ../../ && semgrep scan --config .semgrep/rules/ ./packages/contracts-bedrock # Runs semgrep tests. semgrep-test: - cd ../../ && semgrep scan --test semgrep + cd ../../ && semgrep scan --test --config .semgrep/rules/ .semgrep/tests/ # TODO: Also run lint-forge-tests-check but we need to fix the test names first. # Runs all checks. From b1efd7c52f3f79ed1940ada7c7ade3bdbc6cdc63 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Wed, 6 Nov 2024 16:39:01 -0700 Subject: [PATCH 145/451] ci: Move develop-fault-proofs e2e job onto dedicated runner (#12860) This job is CPU-heavy, and causes other workflows to fail due to resource exhaustion. This PR moves this job onto a dedicated Latitude runner and resource class. The resource class is configured so that it can only run one job at a time, which is fine since this job only runs on commits to develop. --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 680577e5acad..030c15ae6367 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1593,6 +1593,7 @@ workflows: target: test-cannon notify: true mentions: "@proofs-team" + resource_class: ethereum-optimism/latitude-fps-1 requires: - contracts-bedrock-build - cannon-prestate From 3952e601016acf5f3a98b81b27873428d02a207f Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 7 Nov 2024 10:42:19 -0700 Subject: [PATCH 146/451] op-e2e: Fix hanging network notify test (#12868) --- op-node/p2p/sync_test.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/op-node/p2p/sync_test.go b/op-node/p2p/sync_test.go index 7c0b3350df04..2527f48fbc65 100644 --- a/op-node/p2p/sync_test.go +++ b/op-node/p2p/sync_test.go @@ -360,18 +360,24 @@ func TestNetworkNotifyAddPeerAndRemovePeer(t *testing.T) { return nil }, metrics.NoopMetrics, &NoopApplicationScorer{}) - waitChan := make(chan struct{}, 1) + waitChan := make(chan struct{}, 2) + var connectedOnce sync.Once + var disconnectedOnce sync.Once hostA.Network().Notify(&network.NotifyBundle{ ConnectedF: func(nw network.Network, conn network.Conn) { - syncCl.AddPeer(conn.RemotePeer()) - waitChan <- struct{}{} + connectedOnce.Do(func() { + syncCl.AddPeer(conn.RemotePeer()) + waitChan <- struct{}{} + }) }, DisconnectedF: func(nw network.Network, conn network.Conn) { - // only when no connection is available, we can remove the peer - if nw.Connectedness(conn.RemotePeer()) == network.NotConnected { - syncCl.RemovePeer(conn.RemotePeer()) - } - waitChan <- struct{}{} + disconnectedOnce.Do(func() { + // only when no connection is available, we can remove the peer + if nw.Connectedness(conn.RemotePeer()) == network.NotConnected { + syncCl.RemovePeer(conn.RemotePeer()) + } + waitChan <- struct{}{} + }) }, }) syncCl.Start() From dad1087728ceb3021ff2c02b8e153cfc65ea7810 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 7 Nov 2024 12:44:40 -0500 Subject: [PATCH 147/451] feat: Isthmus Contracts (#12746) * feat: Isthmus Contracts work squashed remove unused import fix: IOptimismMintableERC721 interface feat: move semver out of abstract OptimismMintableERC20Factory Also add spacer after _initialized/ing fix: Initializer check in DeployOPChain fix: CrossDomainOwnable3 test feat: update semver-lock feat: Add systemConfigAdmin to deploy-config fix: IL1BlockInterop iface fix: update devnetL1-template fix: casing on systemConfigFeeAdmin feat: update ConfigType names feat: rename setHolocene to setL1BlockValuesIsthmus feat: refine setL1BLockValuesIsthmus to read fee vault config directly from getters. fix: read remoteChainId from ERC721 Factory, not Bridge feat: Update test_setL1BlockValuesIsthmus_isDepositor_succeeds to compare with cfg feat: use setIsthmus() * debug: Add fee admin to intent * fix: fee vault immutable checks no longer necessary * fix: TestConfigDataMarshalUnmarshal * fix: immutable check for L2ProxyAdmin owner * fix: SystemConfigFeeAdmin naming in opchain.go * feat: op-deployer - get SystemConfigFeeAdmin from intent.Roles * remove dead comments * feat: resolve feeAdmin setting todo coment * feat: move unsafeBLockSigner into SystemConfig.Roles struct * feat: move batcherHash into SystemConfig.Roles struct * fix: Add missing feeAdmin setter in DeployOpChain test * fix: SystemConfig.init in specs * feat: resolve todo * feat: resolve some todos * feat: Natspec in StaticConfig * feat: resolve some todos * fix: test_getConfigRoundtripGasPayingToken_succeeds with normalizeSmallString * pre-pr * test: Implement roll and reset prevBought gas in SystemConfig Test's cleanStorageAndInit() * feat: Add natspec on SystemConfig.Roles * feat: Fix all incomplete @notice natspec comments * feat: systemConfigFeeAdmin name in opcm * rebuild snapshots * Add ArtifactsFromURL utility * add configurability to createEnv * clean up env.go * fix apply test * fix: TestInteropDeployment * fix: TestApplyExistingOPCM * fix: unchecked return on checkImmutable * fix: goimports env.go * fix: Add devkey for SystemConfigFeeAdmin * fix: TestInteropDeployment * fix: TestInteropDevRecipe * feat: use base CDM interface in L1Block * feat: configure L1 Block config values in L2Genesis * feat: document Isthmus upgrade transactions on `setIsthmus()` * semver fixes * feat: L2 Genesis with cheated config in L1Block * ok: how'd those warnings get in there in the first place? * chore: update semver-lock * works * allocs build, cfg calls commented out cuz they fail * fix: read DeployConfig before it gets deleted * fix: test_allocs_size * fix: restore l1 deps * fix: test_getConfigRoundtripGasPayingToken_succeeds disallow eth address * fix: update test_proveWithdrawalTransaction_benchmark Unclear why this cost changed? * feat: Add Isthmus to Config.sol * feat: hoist L1Block population above allocs writes * chore: delete debug logs * WIP: update SystemConfig bindings and go usage * fix: go linting * feat: deposit setGasPayingToken as first call in SystemConfig.init * semver lock * TODO in L2Genesis * chore: semver * fix: Remove outdated comments * feat: extract logic into _setNetworkConfig() * test: Add total gas usage test * remove dead comment * ci: reduce heavy fuzz runs temporarily --------- Co-authored-by: Matthew Slipper --- .semgrep/rules/sol-rules.yaml | 4 +- .semgrep/tests/sol-rules.t.sol | 6 + bedrock-devnet/devnet/__init__.py | 2 +- op-chain-ops/devkeys/devkeys.go | 4 + op-chain-ops/genesis/config.go | 3 + .../testdata/test-deploy-config-full.json | 1 + op-chain-ops/interopgen/configs.go | 9 +- op-chain-ops/interopgen/deploy.go | 40 +-- op-chain-ops/interopgen/recipe.go | 13 +- .../deployer/integration_test/apply_test.go | 116 ++++--- op-deployer/pkg/deployer/opcm/opchain.go | 6 +- op-deployer/pkg/deployer/pipeline/opchain.go | 2 +- op-deployer/pkg/deployer/state/intent.go | 6 + op-deployer/pkg/deployer/testutil/env.go | 22 +- op-e2e/bindings/systemconfig.go | 108 ++++++- op-e2e/system/gastoken/gastoken_test.go | 17 +- packages/contracts-bedrock/.gas-snapshot | 20 +- .../deploy-config/devnetL1-template.json | 1 + .../deploy-config/hardhat.json | 1 + packages/contracts-bedrock/foundry.toml | 4 +- .../contracts-bedrock/scripts/Artifacts.s.sol | 2 - .../contracts-bedrock/scripts/L2Genesis.s.sol | 275 ++++++++-------- .../scripts/deploy/ChainAssertions.sol | 61 ++-- .../scripts/deploy/Deploy.s.sol | 24 +- .../scripts/deploy/DeployConfig.s.sol | 2 + .../deploy/DeployImplementations.s.sol | 32 +- .../scripts/deploy/DeployOPChain.s.sol | 23 +- .../scripts/deploy/DeploySuperchain.s.sol | 3 +- .../scripts/libraries/Config.sol | 9 +- .../scripts/libraries/DeployUtils.sol | 10 + .../scripts/libraries/ForgeArtifacts.sol | 19 +- packages/contracts-bedrock/semver-lock.json | 128 ++++---- .../snapshots/abi/BaseFeeVault.json | 48 +-- .../CrossDomainMessengerLegacySpacer0.json | 1 - .../CrossDomainMessengerLegacySpacer1.json | 1 - .../snapshots/abi/L1Block.json | 62 ++++ .../snapshots/abi/L1BlockInterop.json | 46 ++- .../snapshots/abi/L1CrossDomainMessenger.json | 2 +- .../snapshots/abi/L1ERC721Bridge.json | 16 +- .../snapshots/abi/L1FeeVault.json | 48 +-- ...on => L1OptimismMintableERC20Factory.json} | 0 .../snapshots/abi/L1StandardBridge.json | 8 +- .../snapshots/abi/L2CrossDomainMessenger.json | 31 -- .../snapshots/abi/L2ERC721Bridge.json | 33 +- .../abi/L2OptimismMintableERC20Factory.json | 196 ++++++++++++ .../snapshots/abi/L2ProxyAdmin.json | 300 ++++++++++++++++++ .../snapshots/abi/L2StandardBridge.json | 37 +-- .../abi/L2StandardBridgeInterop.json | 32 +- .../snapshots/abi/OPContractsManager.json | 5 + .../abi/OPContractsManagerInterop.json | 5 + .../snapshots/abi/OptimismMintableERC721.json | 6 +- .../abi/OptimismMintableERC721Factory.json | 26 +- .../snapshots/abi/OptimismPortal.json | 22 +- .../snapshots/abi/OptimismPortal2.json | 40 ++- .../snapshots/abi/OptimismPortalInterop.json | 48 ++- .../snapshots/abi/SequencerFeeVault.json | 50 +-- .../snapshots/abi/SuperchainConfig.json | 31 ++ .../snapshots/abi/SystemConfig.json | 84 ++++- .../snapshots/abi/SystemConfigInterop.json | 132 ++++++-- .../CrossDomainMessengerLegacySpacer0.json | 9 - .../CrossDomainMessengerLegacySpacer1.json | 65 ---- .../snapshots/storageLayout/L1Block.json | 7 + .../storageLayout/L1BlockInterop.json | 9 +- .../storageLayout/L1CrossDomainMessenger.json | 62 +++- .../storageLayout/L1ERC721Bridge.json | 35 +- ...on => L1OptimismMintableERC20Factory.json} | 54 ++-- .../storageLayout/L1StandardBridge.json | 60 ++-- .../storageLayout/L2CrossDomainMessenger.json | 41 ++- .../storageLayout/L2ERC721Bridge.json | 28 +- .../L2OptimismMintableERC20Factory.json | 30 ++ .../snapshots/storageLayout/L2ProxyAdmin.json | 37 +++ .../storageLayout/L2StandardBridge.json | 32 +- .../L2StandardBridgeInterop.json | 32 +- .../src/L1/L1CrossDomainMessenger.sol | 25 +- .../src/L1/L1ERC721Bridge.sol | 30 +- .../src/L1/L1OptimismMintableERC20Factory.sol | 41 +++ .../src/L1/L1StandardBridge.sol | 44 ++- .../src/L1/OPContractsManager.sol | 25 +- .../src/L1/OPContractsManagerInterop.sol | 9 +- .../src/L1/OptimismPortal.sol | 18 +- .../src/L1/OptimismPortal2.sol | 41 ++- .../src/L1/OptimismPortalInterop.sol | 35 +- .../src/L1/SuperchainConfig.sol | 30 +- .../contracts-bedrock/src/L1/SystemConfig.sol | 175 +++++++--- .../src/L1/SystemConfigInterop.sol | 54 +--- .../L1/interfaces/IL1CrossDomainMessenger.sol | 4 + .../src/L1/interfaces/IL1ERC721Bridge.sol | 6 + .../IL1OptimismMintableERC20Factory.sol | 38 +++ .../src/L1/interfaces/IL1StandardBridge.sol | 5 +- .../src/L1/interfaces/IOptimismPortal.sol | 3 +- .../src/L1/interfaces/IOptimismPortal2.sol | 4 +- .../L1/interfaces/IOptimismPortalInterop.sol | 5 +- .../src/L1/interfaces/ISuperchainConfig.sol | 4 +- .../src/L1/interfaces/ISystemConfig.sol | 22 +- .../L1/interfaces/ISystemConfigInterop.sol | 5 +- .../contracts-bedrock/src/L2/BaseFeeVault.sol | 30 +- .../contracts-bedrock/src/L2/FeeVault.sol | 91 +++--- .../src/L2/GasPriceOracle.sol | 4 +- packages/contracts-bedrock/src/L2/L1Block.sol | 151 ++++++++- .../src/L2/L1BlockInterop.sol | 36 +-- .../contracts-bedrock/src/L2/L1FeeVault.sol | 30 +- .../src/L2/L2CrossDomainMessenger.sol | 27 +- .../src/L2/L2ERC721Bridge.sol | 30 +- .../src/L2/L2OptimismMintableERC20Factory.sol | 25 ++ .../contracts-bedrock/src/L2/L2ProxyAdmin.sol | 17 + .../src/L2/L2StandardBridge.sol | 26 +- .../src/L2/L2StandardBridgeInterop.sol | 4 +- .../src/L2/L2ToL2CrossDomainMessenger.sol | 4 +- .../OptimismMintableERC721Factory.sol | 52 +-- .../src/L2/OptimismSuperchainERC20.sol | 4 +- .../src/L2/OptimismSuperchainERC20Beacon.sol | 4 +- .../src/L2/OptimismSuperchainERC20Factory.sol | 4 +- .../src/L2/SequencerFeeVault.sol | 36 ++- .../src/L2/SuperchainWETH.sol | 4 +- packages/contracts-bedrock/src/L2/WETH.sol | 4 +- .../src/L2/interfaces/IBaseFeeVault.sol | 16 +- .../src/L2/interfaces/IFeeVault.sol | 8 +- .../src/L2/interfaces/IL1Block.sol | 7 + .../src/L2/interfaces/IL1BlockInterop.sol | 12 +- .../src/L2/interfaces/IL1FeeVault.sol | 15 +- .../L2/interfaces/IL2CrossDomainMessenger.sol | 3 +- .../src/L2/interfaces/IL2ERC721Bridge.sol | 3 +- .../IL2OptimismMintableERC20Factory.sol | 36 +++ .../src/L2/interfaces/IL2StandardBridge.sol | 8 +- .../interfaces/IL2StandardBridgeInterop.sol | 10 +- .../IOptimismMintableERC721Factory.sol | 12 +- .../src/L2/interfaces/ISequencerFeeVault.sol | 18 +- .../src/libraries/Encoding.sol | 36 +++ .../src/libraries/Predeploys.sol | 4 +- .../src/libraries/StaticConfig.sol | 58 ++++ .../contracts-bedrock/src/libraries/Types.sol | 25 ++ .../src/universal/CrossDomainMessenger.sol | 108 ++++--- .../src/universal/ERC721Bridge.sol | 51 ++- .../OptimismMintableERC20Factory.sol | 43 +-- .../src/universal/OptimismMintableERC721.sol | 17 +- .../src/universal/StandardBridge.sol | 70 ++-- .../interfaces/ICrossDomainMessenger.sol | 3 +- .../universal/interfaces/IERC721Bridge.sol | 1 - .../IOptimismMintableERC20Factory.sol | 2 - .../interfaces/IOptimismMintableERC721.sol | 10 +- .../universal/interfaces/IStandardBridge.sol | 3 +- .../test/L1/OPContractsManager.t.sol | 3 +- .../test/L1/OptimismPortal.t.sol | 16 +- .../test/L1/OptimismPortal2.t.sol | 116 ++++++- .../test/L1/OptimismPortalInterop.t.sol | 73 ++--- .../test/L1/SuperchainConfig.t.sol | 9 +- .../test/L1/SystemConfig.t.sol | 196 ++++++++++-- .../test/L1/SystemConfigInterop.t.sol | 17 +- .../test/L2/BaseFeeVault.t.sol | 2 +- .../test/L2/CrossDomainOwnable2.t.sol | 13 - .../test/L2/CrossDomainOwnable3.t.sol | 2 +- .../contracts-bedrock/test/L2/FeeVault.t.sol | 21 ++ .../contracts-bedrock/test/L2/L1Block.t.sol | 145 +++++++++ .../test/L2/L1BlockInterop.t.sol | 37 ++- .../test/L2/L1FeeVault.t.sol | 2 +- .../test/L2/L2CrossDomainMessenger.t.sol | 8 +- .../test/L2/L2ERC721Bridge.t.sol | 4 +- .../contracts-bedrock/test/L2/L2Genesis.t.sol | 4 +- .../test/L2/L2StandardBridge.t.sol | 10 +- .../OptimismMintableERC721Factory.t.sol | 9 +- .../test/L2/Predeploys.t.sol | 17 +- .../test/L2/SequencerFeeVault.t.sol | 71 ++--- .../test/invariants/SystemConfig.t.sol | 18 +- .../test/libraries/Encoding.t.sol | 11 + .../test/opcm/DeployOPChain.t.sol | 26 +- .../contracts-bedrock/test/setup/Setup.sol | 76 ++++- .../OptimismMintableERC20Factory.t.sol | 10 +- .../universal/OptimismMintableERC721.t.sol | 6 +- .../test/universal/Specs.t.sol | 56 +++- .../test/universal/StandardBridge.t.sol | 10 + .../test/vendor/Initializable.t.sol | 79 ++--- 171 files changed, 3733 insertions(+), 1903 deletions(-) delete mode 100644 packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json delete mode 100644 packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json rename packages/contracts-bedrock/snapshots/abi/{OptimismMintableERC20Factory.json => L1OptimismMintableERC20Factory.json} (100%) create mode 100644 packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json create mode 100644 packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json delete mode 100644 packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json delete mode 100644 packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json rename packages/contracts-bedrock/snapshots/storageLayout/{OptimismMintableERC20Factory.json => L1OptimismMintableERC20Factory.json} (64%) create mode 100644 packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json create mode 100644 packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json create mode 100644 packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol create mode 100644 packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol create mode 100644 packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol create mode 100644 packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol rename packages/contracts-bedrock/src/{universal => L2}/OptimismMintableERC721Factory.sol (51%) create mode 100644 packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol rename packages/contracts-bedrock/src/{universal => L2}/interfaces/IOptimismMintableERC721Factory.sol (66%) create mode 100644 packages/contracts-bedrock/test/L2/FeeVault.t.sol rename packages/contracts-bedrock/test/{universal => L2}/OptimismMintableERC721Factory.t.sol (89%) diff --git a/.semgrep/rules/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml index e0d83414c4bd..e801bd9be25f 100644 --- a/.semgrep/rules/sol-rules.yaml +++ b/.semgrep/rules/sol-rules.yaml @@ -131,7 +131,7 @@ rules: - pattern-not: require($ERR); - focus-metavariable: $ERR - pattern-not-regex: \"(\w+:\s[^"]+)\" - - pattern-not-regex: string\.concat\(\"(\w+:\s[^"]+)\"\,[^"]+\) + - pattern-not-regex: string\.concat\(\"(\w+:\s)\"[^)]+\) - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: @@ -152,7 +152,7 @@ rules: - pattern-not: revert $ERR(...); - focus-metavariable: $MSG - pattern-not-regex: \"(\w+:\s[^"]+)\" - - pattern-not-regex: string\.concat\(\"(\w+:\s[^"]+)\"\,[^"]+\) + - pattern-not-regex: string\.concat\(\"(\w+:\s)\"[^)]+\) - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: diff --git a/.semgrep/tests/sol-rules.t.sol b/.semgrep/tests/sol-rules.t.sol index 3b4329f7e388..be4816dc3931 100644 --- a/.semgrep/tests/sol-rules.t.sol +++ b/.semgrep/tests/sol-rules.t.sol @@ -497,6 +497,9 @@ contract SemgrepTest__sol_style_malformed_require { ) ); + // ok: sol-style-malformed-require + require(result.length > 0, string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); + // ruleid: sol-style-malformed-require require(cond, "MyContract: "); @@ -541,6 +544,9 @@ contract SemgrepTest__sol_style_malformed_revert { ) ); + // ok: sol-style-malformed-revert + revert(string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); + // ruleid: sol-style-malformed-revert revert("MyContract: "); diff --git a/bedrock-devnet/devnet/__init__.py b/bedrock-devnet/devnet/__init__.py index 70243dc790c8..7bf5c6a2595c 100644 --- a/bedrock-devnet/devnet/__init__.py +++ b/bedrock-devnet/devnet/__init__.py @@ -24,7 +24,7 @@ log = logging.getLogger() # Global constants -FORKS = ["delta", "ecotone", "fjord", "granite", "holocene"] +FORKS = ["delta", "ecotone", "fjord", "granite", "holocene", "isthmus"] # Global environment variables DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true" diff --git a/op-chain-ops/devkeys/devkeys.go b/op-chain-ops/devkeys/devkeys.go index fdb151e4e6fc..1eb011b07eaa 100644 --- a/op-chain-ops/devkeys/devkeys.go +++ b/op-chain-ops/devkeys/devkeys.go @@ -158,6 +158,8 @@ const ( SequencerFeeVaultRecipientRole ChainOperatorRole = 9 // SystemConfigOwner is the key that can make SystemConfig changes. SystemConfigOwner ChainOperatorRole = 10 + // SystemConfigFeeAdmin is the key that can make SystemConfigFee changes. + SystemConfigFeeAdmin ChainOperatorRole = 11 ) func (role ChainOperatorRole) String() string { @@ -184,6 +186,8 @@ func (role ChainOperatorRole) String() string { return "sequencer-fee-vault-recipient" case SystemConfigOwner: return "system-config-owner" + case SystemConfigFeeAdmin: + return "system-config-fee-admin" default: return fmt.Sprintf("unknown-operator-%d", uint64(role)) } diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 7ed0a6e2f682..8446241a9417 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -286,6 +286,9 @@ type OperatorDeployConfig struct { // BatchSenderAddress represents the initial sequencer account that authorizes batches. // Transactions sent from this account to the batch inbox address are considered valid. BatchSenderAddress common.Address `json:"batchSenderAddress"` + + // SystemConfigfeeAdmin is the address of the account that has the ability to set the fee parameters. + SystemConfigfeeAdmin common.Address `json:"systemConfigFeeAdmin"` } var _ ConfigChecker = (*OperatorDeployConfig)(nil) diff --git a/op-chain-ops/genesis/testdata/test-deploy-config-full.json b/op-chain-ops/genesis/testdata/test-deploy-config-full.json index 814fff245b0d..da1e346c407b 100644 --- a/op-chain-ops/genesis/testdata/test-deploy-config-full.json +++ b/op-chain-ops/genesis/testdata/test-deploy-config-full.json @@ -22,6 +22,7 @@ "l1GenesisBlockGasLimit": "0x1c9c380", "l1GenesisBlockDifficulty": "0x1", "finalSystemOwner": "0xbcd4042de499d14e55001ccbb24a551f3b954096", + "systemConfigFeeAdmin": "0xbcd4042de499d14e55001ccbb24a551f3b954096", "superchainConfigGuardian": "0x0000000000000000000000000000000000000112", "finalizationPeriodSeconds": 2, "l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index 942588a9929c..e3fca23a629e 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -71,10 +71,11 @@ func (c *SuperchainConfig) Check(log log.Logger) error { } type L2Config struct { - Deployer common.Address // account used to deploy contracts to L2 - Proposer common.Address - Challenger common.Address - SystemConfigOwner common.Address + Deployer common.Address // account used to deploy contracts to L2 + Proposer common.Address + Challenger common.Address + SystemConfigOwner common.Address + SystemConfigFeeAdmin common.Address genesis.L2InitializationConfig Prefund map[common.Address]*big.Int SaltMixer string diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 476406821ea5..10f361b643ac 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -200,25 +200,27 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) - output, err := opcm.DeployOPChainV160(l1Host, opcm.DeployOPChainInputV160{ - OpChainProxyAdminOwner: cfg.ProxyAdminOwner, - SystemConfigOwner: cfg.SystemConfigOwner, - Batcher: cfg.BatchSenderAddress, - UnsafeBlockSigner: cfg.P2PSequencerAddress, - Proposer: cfg.Proposer, - Challenger: cfg.Challenger, - BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, - BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, - L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), - OpcmProxy: superDeployment.OpcmProxy, - SaltMixer: cfg.SaltMixer, - GasLimit: cfg.GasLimit, - DisputeGameType: cfg.DisputeGameType, - DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, - DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, - DisputeSplitDepth: cfg.DisputeSplitDepth, - DisputeClockExtension: cfg.DisputeClockExtension, - DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, + output, err := opcm.DeployOPChainIsthmus(l1Host, opcm.DeployOPChainInputIsthmus{ + DeployOPChainInputV160: opcm.DeployOPChainInputV160{ + OpChainProxyAdminOwner: cfg.ProxyAdminOwner, + SystemConfigOwner: cfg.SystemConfigOwner, + Batcher: cfg.BatchSenderAddress, + UnsafeBlockSigner: cfg.P2PSequencerAddress, + Proposer: cfg.Proposer, + Challenger: cfg.Challenger, + BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, + BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, + L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), + OpcmProxy: superDeployment.OpcmProxy, + SaltMixer: cfg.SaltMixer, + GasLimit: cfg.GasLimit, + DisputeGameType: cfg.DisputeGameType, + DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, + DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, + DisputeSplitDepth: cfg.DisputeSplitDepth, + DisputeClockExtension: cfg.DisputeClockExtension, + DisputeMaxClockDuration: cfg.DisputeMaxClockDuration}, + SystemConfigFeeAdmin: cfg.SystemConfigFeeAdmin, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index 8983ee72da8e..06bcf8bfca03 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -178,12 +178,17 @@ func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (* if err != nil { return nil, err } + systemConfigFeeAdmin, err := addrs.Address(chainOps(devkeys.SystemConfigFeeAdmin)) + if err != nil { + return nil, err + } l2Cfg := &L2Config{ - Deployer: deployer, - Proposer: proposer, - Challenger: challenger, - SystemConfigOwner: systemConfigOwner, + Deployer: deployer, + Proposer: proposer, + Challenger: challenger, + SystemConfigOwner: systemConfigOwner, + SystemConfigFeeAdmin: systemConfigFeeAdmin, L2InitializationConfig: genesis.L2InitializationConfig{ DevDeployConfig: genesis.DevDeployConfig{ FundDevAccounts: true, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index fa59c49dc8ac..919b717cc055 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -12,6 +12,7 @@ import ( "time" altda "github.com/ethereum-optimism/optimism/op-alt-da" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -125,7 +126,7 @@ func TestEndToEndApply(t *testing.T) { }) require.NoError(t, err) - env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) + env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr, localArtifactsFactory, localArtifactsFactory) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) cg := ethClientCodeGetter(ctx, l1Client) @@ -145,7 +146,7 @@ func TestEndToEndApply(t *testing.T) { t.Run("subsequent chain", func(t *testing.T) { // create a new environment with wiped state to ensure we can continue using the // state from the previous deployment - env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr) + env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr, localArtifactsFactory, localArtifactsFactory) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2)) require.NoError(t, deployer.ApplyPipeline( @@ -207,7 +208,17 @@ func TestApplyExistingOPCM(t *testing.T) { }) require.NoError(t, err) - env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) + // use the l2 contracts here because the v1.7.0 contracts are compatible with the v1.6.0 + // contracts and createEnv uses the same artifacts for both L1/L2 in the bundle. + env, bundle, _ := createEnv( + t, + lgr, + l1Client, + bcaster, + deployerAddr, + taggedArtifactsFactory(standard.DefaultL1ContractsTag), + taggedArtifactsFactory(standard.DefaultL2ContractsTag), + ) intent, st := newIntent( t, @@ -379,7 +390,8 @@ func TestProofParamOverrides(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) + err := checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) + require.NoError(t, err) }) } } @@ -403,9 +415,9 @@ func TestInteropDeployment(t *testing.T) { chainState := st.Chains[0] depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") - checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) - proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes()) - checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) + require.NoError(t, checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot)) + systemConfigOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes()) + checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, systemConfigOwnerHash) } func TestAltDADeployment(t *testing.T) { @@ -512,7 +524,7 @@ func TestInvalidL2Genesis(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr, localArtifactsFactory, localArtifactsFactory) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis @@ -546,27 +558,42 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, * loc, _ := testutil.LocalArtifacts(t) - env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr, localArtifactsFactory, localArtifactsFactory) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis return env, bundle, intent, st } +type artifactsFactory func(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) + +func localArtifactsFactory(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { + return testutil.LocalArtifacts(t) +} + +func taggedArtifactsFactory(tag string) artifactsFactory { + return func(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { + return testutil.ArtifactsFromURL(t, fmt.Sprintf("tag://%s", tag)) + } +} + func createEnv( t *testing.T, lgr log.Logger, l1Client *ethclient.Client, bcaster broadcaster.Broadcaster, deployerAddr common.Address, + l1Factory artifactsFactory, + l2Factory artifactsFactory, ) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) { - _, artifactsFS := testutil.LocalArtifacts(t) + _, l1AFS := l1Factory(t) + _, l2AFS := l2Factory(t) host, err := env.DefaultScriptHost( bcaster, lgr, deployerAddr, - artifactsFS, + l1AFS, 0, ) require.NoError(t, err) @@ -581,8 +608,8 @@ func createEnv( } bundle := pipeline.ArtifactsBundle{ - L1: artifactsFS, - L2: artifactsFS, + L1: l1AFS, + L2: l2AFS, } return env, bundle, host @@ -632,13 +659,14 @@ func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.In Eip1559Denominator: 50, Eip1559Elasticity: 6, Roles: state.ChainRoles{ - L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), - UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), - Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), - Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), - Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), + L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), + SystemConfigFeeAdmin: addrFor(t, dk, devkeys.SystemConfigFeeAdmin.Key(l1ChainID)), + UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), + Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), + Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), + Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), }, } } @@ -739,17 +767,20 @@ func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, int alloc := chainState.Allocs.Data.Accounts chainIntent := intent.Chains[i] - checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) - checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) - - // ownership slots - var addrAsSlot common.Hash - addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes()) - // slot 0 - ownerSlot := common.Hash{} - checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot) + + // First try to read the owner from the bytecode, which is the situation with the + // Isthmus L2ProxyAdmin (on this commit). + if err := checkImmutableBehindProxy(t, alloc, predeploys.ProxyAdminAddr, common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001")); err != nil { + t.Logf("Warning: Failed to check immutable behind proxy for L2ProxyAdmin, falling back to <=v1.6.0 storage check") + // If the bytecode check fails, fall back to reading the owner from storage. + // Note however that the L2ProxyAdmin owner address here (0xe59a881b2626f948f56f509f180c32428585629a) comes from the chainIntent, + // and does not actually match the `owner()` of the L2ProxyAdmin contract deployed on Sepolia (0x2FC3ffc903729a0f03966b917003800B145F67F3). + // It seems the L2 state is locally constructed rather than pulled from an L2 RPC. + var L2ProxyAdminOwner common.Hash + L2ProxyAdminOwner.SetBytes(chainIntent.Roles.L2ProxyAdminOwner.Bytes()) + checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, common.Hash{}, L2ProxyAdminOwner) + } + var defaultGovOwner common.Hash defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes()) checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner) @@ -770,20 +801,23 @@ type bytesMarshaler interface { Bytes() []byte } -func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { +func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) error { implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) - checkImmutable(t, allocations, implementationAddress, thing) + return checkImmutable(t, allocations, implementationAddress, thing) } -func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { +func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) error { account, ok := allocations[implementationAddress] - require.True(t, ok, "%s not found in allocations", implementationAddress) - require.NotEmpty(t, account.Code, "%s should have code", implementationAddress) - require.True( - t, - bytes.Contains(account.Code, thing.Bytes()), - "%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()), - ) + if !ok { + return fmt.Errorf("%s not found in allocations", implementationAddress) + } + if len(account.Code) == 0 { + return fmt.Errorf("%s should have code", implementationAddress) + } + if !bytes.Contains(account.Code, thing.Bytes()) { + return fmt.Errorf("%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes())) + } + return nil } func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) { diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 537765ce5154..c8c803d72d4f 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -59,7 +59,7 @@ type opcmDeployInputV160 struct { type opcmRolesIsthmus struct { opcmRolesBase - FeeAdmin common.Address + SystemConfigFeeAdmin common.Address } type opcmDeployInputIsthmus struct { @@ -135,8 +135,8 @@ func DeployOPChainInputIsthmusDeployCalldata(input DeployOPChainInputIsthmus) an v160Data := DeployOPChainInputV160DeployCalldata(input.DeployOPChainInputV160).(opcmDeployInputV160) return opcmDeployInputIsthmus{ Roles: opcmRolesIsthmus{ - opcmRolesBase: v160Data.Roles, - FeeAdmin: input.SystemConfigFeeAdmin, + opcmRolesBase: v160Data.Roles, + SystemConfigFeeAdmin: input.SystemConfigFeeAdmin, }, opcmDeployInputBase: v160Data.opcmDeployInputBase, } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 59e5d214cf4f..72e4703e0503 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -276,7 +276,7 @@ func makeDCIIsthmus(intent *state.Intent, thisIntent *state.ChainIntent, chainID return opcm.DeployOPChainInputIsthmus{ DeployOPChainInputV160: dci, - SystemConfigFeeAdmin: common.Address{'D', 'E', 'A', 'D'}, + SystemConfigFeeAdmin: thisIntent.Roles.SystemConfigFeeAdmin, }, nil } diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 860944666e9e..2459ddbec528 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -176,6 +176,8 @@ type ChainRoles struct { SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"` + SystemConfigFeeAdmin common.Address `json:"systemConfigFeeAdmin" toml:"systemConfigFeeAdmin"` + UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"` Batcher common.Address `json:"batcher" toml:"batcher"` @@ -199,6 +201,10 @@ func (c *ChainIntent) Check() error { c.Roles.SystemConfigOwner = c.Roles.L1ProxyAdminOwner } + if c.Roles.SystemConfigFeeAdmin == emptyAddress { + return fmt.Errorf("systemConfigFeeAdmin must be set") + } + if c.Roles.L2ProxyAdminOwner == emptyAddress { return fmt.Errorf("l2ProxyAdminOwner must be set") } diff --git a/op-deployer/pkg/deployer/testutil/env.go b/op-deployer/pkg/deployer/testutil/env.go index 6b289c4503b7..a1731fb34b35 100644 --- a/op-deployer/pkg/deployer/testutil/env.go +++ b/op-deployer/pkg/deployer/testutil/env.go @@ -3,31 +3,35 @@ package testutil import ( "context" "fmt" - "net/url" "path" "runtime" "testing" - artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" op_service "github.com/ethereum-optimism/optimism/op-service" "github.com/stretchr/testify/require" ) -func LocalArtifacts(t *testing.T) (*artifacts2.Locator, foundry.StatDirFs) { +func LocalArtifacts(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { _, testFilename, _, ok := runtime.Caller(0) require.Truef(t, ok, "failed to get test filename") monorepoDir, err := op_service.FindMonorepoRoot(testFilename) require.NoError(t, err) artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) - require.NoError(t, err) - loc := &artifacts2.Locator{ - URL: artifactsURL, - } + return ArtifactsFromURL(t, fmt.Sprintf("file://%s", artifactsDir)) +} + +func ArtifactsFromURL(t *testing.T, artifactsURLStr string) (*artifacts.Locator, foundry.StatDirFs) { + loc := new(artifacts.Locator) + require.NoError(t, loc.UnmarshalText([]byte(artifactsURLStr))) - artifactsFS, cleanupArtifacts, err := artifacts2.Download(context.Background(), loc, artifacts2.NoopDownloadProgressor) + progressor := artifacts.LogProgressor(testlog.Logger(t, log.LevelInfo)) + artifactsFS, cleanupArtifacts, err := artifacts.Download(context.Background(), loc, progressor) require.NoError(t, err) t.Cleanup(func() { _ = cleanupArtifacts() diff --git a/op-e2e/bindings/systemconfig.go b/op-e2e/bindings/systemconfig.go index a6ff5ce1480e..9b845fa5e19e 100644 --- a/op-e2e/bindings/systemconfig.go +++ b/op-e2e/bindings/systemconfig.go @@ -49,15 +49,45 @@ type SystemConfigAddresses struct { GasPayingToken common.Address } +// SystemConfigRoles is an auto generated low-level Go binding around an user-defined struct. +type SystemConfigRoles struct { + Owner common.Address + FeeAdmin common.Address + UnsafeBlockSigner common.Address + BatcherHash [32]byte +} + // SystemConfigMetaData contains all meta data concerning the SystemConfig contract. var SystemConfigMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"BATCH_INBOX_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"DISPUTE_GAME_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_CROSS_DOMAIN_MESSENGER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_ERC_721_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_STANDARD_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_PORTAL_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"START_BLOCK_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UNSAFE_BLOCK_SIGNER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"basefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchInbox\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batcherHash\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blobbasefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disputeGameFactory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Denominator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Elasticity\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingToken\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"decimals_\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenName\",\"inputs\":[],\"outputs\":[{\"name\":\"name_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenSymbol\",\"inputs\":[],\"outputs\":[{\"name\":\"symbol_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_config\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]},{\"name\":\"_batchInbox\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_addresses\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Addresses\",\"components\":[{\"name\":\"l1CrossDomainMessenger\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1ERC721Bridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1StandardBridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"disputeGameFactory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismPortal\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismMintableERC20Factory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasPayingToken\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isCustomGasToken\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1CrossDomainMessenger\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1ERC721Bridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1StandardBridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maximumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"minimumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismMintableERC20Factory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismPortal\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"overhead\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"resourceConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"scalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setBatcherHash\",\"inputs\":[{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEIP1559Params\",\"inputs\":[{\"name\":\"_denominator\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_elasticity\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfig\",\"inputs\":[{\"name\":\"_overhead\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_scalar\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfigEcotone\",\"inputs\":[{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasLimit\",\"inputs\":[{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnsafeBlockSigner\",\"inputs\":[{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"startBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"startBlock_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unsafeBlockSigner\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"ConfigUpdate\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"updateType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"BATCH_INBOX_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"DISPUTE_GAME_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_CROSS_DOMAIN_MESSENGER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_ERC_721_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_STANDARD_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_PORTAL_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"START_BLOCK_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UNSAFE_BLOCK_SIGNER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"basefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchInbox\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batcherHash\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blobbasefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disputeGameFactory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Denominator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Elasticity\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"feeAdmin\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingToken\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"decimals_\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenName\",\"inputs\":[],\"outputs\":[{\"name\":\"name_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenSymbol\",\"inputs\":[],\"outputs\":[{\"name\":\"symbol_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_roles\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Roles\",\"components\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"feeAdmin\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_config\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]},{\"name\":\"_batchInbox\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_addresses\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Addresses\",\"components\":[{\"name\":\"l1CrossDomainMessenger\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1ERC721Bridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1StandardBridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"disputeGameFactory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismPortal\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismMintableERC20Factory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasPayingToken\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isCustomGasToken\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1CrossDomainMessenger\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1ERC721Bridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1StandardBridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maximumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"minimumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismMintableERC20Factory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismPortal\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"overhead\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"resourceConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"scalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setBatcherHash\",\"inputs\":[{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEIP1559Params\",\"inputs\":[{\"name\":\"_denominator\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_elasticity\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setFeeVaultConfig\",\"inputs\":[{\"name\":\"_type\",\"type\":\"uint8\",\"internalType\":\"enumTypes.ConfigType\"},{\"name\":\"_recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_min\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_network\",\"type\":\"uint8\",\"internalType\":\"enumTypes.WithdrawalNetwork\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfig\",\"inputs\":[{\"name\":\"_overhead\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_scalar\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfigEcotone\",\"inputs\":[{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasLimit\",\"inputs\":[{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnsafeBlockSigner\",\"inputs\":[{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"startBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"startBlock_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unsafeBlockSigner\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"ConfigUpdate\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"updateType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"UnsafeCast\",\"inputs\":[]}]", + Bin: "0x60806040523480156200001157600080fd5b506200005a6200004360017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a062000130565b60001b6000196200006a60201b6200119d1760201c565b620000646200006e565b62000156565b9055565b600054610100900460ff1615620000db5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200012e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000828210156200015157634e487b7160e01b600052601160045260246000fd5b500390565b612fd880620001666000396000f3fe608060405234801561001057600080fd5b50600436106103155760003560e01c8063a7119869116101a7578063dac6e63a116100ee578063f2fde38b11610097578063f8c68de011610071578063f8c68de01461076f578063fd32aa0f14610777578063ffa1ad741461077f57600080fd5b8063f2fde38b1461073f578063f45e65d814610752578063f68016b71461075b57600080fd5b8063e81b2c6d116100c8578063e81b2c6d1461070e578063ec70751714610717578063f2b4e6171461073757600080fd5b8063dac6e63a146106f6578063e0e2016d146106fe578063e2a3285c1461070657600080fd5b8063c4e8ddfa11610150578063cc731b021161012a578063cc731b02146105aa578063d220a9e0146106de578063d8444715146106ee57600080fd5b8063c4e8ddfa14610577578063c9b26f611461057f578063c9ff2d161461059257600080fd5b8063be4be78311610181578063be4be78314610520578063bfb14fb714610533578063c0fd4b411461056457600080fd5b8063a7119869146104fd578063b40a817c14610505578063bc49ce5f1461051857600080fd5b806330a402c01161026b578063550fcdc9116102145780638da5cb5b116101ee5780638da5cb5b146104c4578063935f029e146104e25780639b7d7f0a146104f557600080fd5b8063550fcdc9146104ac5780635d73369c146104b4578063715018a6146104bc57600080fd5b80634add321d116102455780634add321d1461043b5780634f16540b1461044357806354fd4d501461046a57600080fd5b806330a402c0146103f55780634397dfef146103fd57806348cd4cb11461043357600080fd5b806318d13918116102cd57806321326849116102a757806321326849146103b757806321d7fde5146103cf57806322b4dded146103e257600080fd5b806318d139181461039257806319f5cea8146103a75780631fd19ee1146103af57600080fd5b80630a49cb03116102fe5780630a49cb03146103625780630ae14b1b1461036a5780630c18c1621461038957600080fd5b806306c926571461031a578063078f29cf14610335575b600080fd5b610322610787565b6040519081526020015b60405180910390f35b61033d6107b5565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161032c565b61033d6107ee565b630bebc2005b60405167ffffffffffffffff909116815260200161032c565b61032260655481565b6103a56103a03660046128f3565b61081e565b005b610322610832565b61033d61085d565b6103bf610887565b604051901515815260200161032c565b6103a56103dd366004612929565b6108c6565b6103a56103f0366004612b4c565b6108dc565b61033d610d21565b610405610d4b565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260ff90911660208301520161032c565b610322610d5f565b610370610d8f565b6103227f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c0881565b60408051808201909152600c81527f322e332e302d626574612e36000000000000000000000000000000000000000060208201525b60405161032c9190612c93565b61049f610db5565b610322610dbf565b6103a5610dea565b60335473ffffffffffffffffffffffffffffffffffffffff1661033d565b6103a56104f0366004612ca6565b610dfe565b61033d610e10565b61033d610e40565b6103a5610513366004612cc8565b610e70565b610322610e81565b6103a561052e366004612ce3565b610eac565b60685461054f9068010000000000000000900463ffffffff1681565b60405163ffffffff909116815260200161032c565b6103a5610572366004612929565b610f80565b61033d610f92565b6103a561058d366004612d3c565b610fc2565b606a5461054f90640100000000900463ffffffff1681565b61066e6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c08101825260695463ffffffff8082168352640100000000820460ff9081166020850152650100000000008304169383019390935266010000000000008104831660608301526a0100000000000000000000810490921660808201526e0100000000000000000000000000009091046fffffffffffffffffffffffffffffffff1660a082015290565b60405161032c9190600060c08201905063ffffffff80845116835260ff602085015116602084015260ff6040850151166040840152806060850151166060840152806080850151166080840152506fffffffffffffffffffffffffffffffff60a08401511660a083015292915050565b606a5461054f9063ffffffff1681565b61049f610fd3565b61033d610fdd565b61032261100d565b610322611038565b61032260675481565b60685461054f906c01000000000000000000000000900463ffffffff1681565b61033d611063565b6103a561074d3660046128f3565b611093565b61032260665481565b6068546103709067ffffffffffffffff1681565b610322611147565b610322611172565b610322600081565b6107b260017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b81565b60006107e96107e560017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b5490565b905090565b60006107e96107e560017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b6108266111a1565b61082f81611222565b50565b6107b260017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b60006107e97f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c085490565b600080610892610d4b565b5073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141592915050565b6108ce6111a1565b6108d882826112df565b5050565b600054610100900460ff16158080156108fc5750600054600160ff909116105b806109165750303b158015610916575060005460ff166001145b6109a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610a0557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610a0d61141e565b8751610a1890611093565b610a2588606001516114bd565b610a2f87876112df565b610a38856114e5565b60408801517f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c085560208801517f5a12cb4aeab79fd7e76814330f3e0732946417b36fa088add63d298d3c0f7a3f55610ab9610ab460017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b849055565b610af0610ae760017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b60808401519055565b610b27610b1e60017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b60608401519055565b610b5e610b5560017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b60a08401519055565b610b95610b8c60017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b8351600461163b565b610bcf610bc360017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b6020840151600561163b565b610c09610bfd60017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b6040840151600661163b565b610c116116f2565b610c19611784565b610c268260c001516117ec565b610c2f84611ae0565b610c37610d8f565b67ffffffffffffffff168567ffffffffffffffff161015610cb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b8015610d1757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b60006107e97f5a12cb4aeab79fd7e76814330f3e0732946417b36fa088add63d298d3c0f7a3f5490565b600080610d56611f54565b90939092509050565b60006107e96107e560017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b6069546000906107e99063ffffffff6a0100000000000000000000820481169116612d9b565b60606107e9611fd1565b6107b260017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b610df26111a1565b610dfc6000612092565b565b610e066111a1565b6108d88282612109565b60006107e96107e560017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b60006107e96107e560017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b610e786111a1565b61082f816114e5565b6107b260017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b610eb4610d21565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f53797374656d436f6e6669673a2063616c6c6572206973206e6f74207468652060448201527f6665652061646d696e0000000000000000000000000000000000000000000000606482015260840161099e565b610f7a848484846121df565b50505050565b610f886111a1565b6108d88282612353565b60006107e96107e560017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b610fca6111a1565b61082f816114bd565b60606107e9612525565b60006107e96107e560017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b6107b260017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b6107b260017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b60006107e96107e560017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b61109b6111a1565b73ffffffffffffffffffffffffffffffffffffffff811661113e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161099e565b61082f81612092565b6107b260017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b6107b260017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b9055565b60335473ffffffffffffffffffffffffffffffffffffffff163314610dfc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161099e565b61124b7f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08829055565b6040805173ffffffffffffffffffffffffffffffffffffffff8316602082015260009101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060035b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516112d39190612c93565b60405180910390a35050565b606880547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000063ffffffff8581169182027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16929092176c0100000000000000000000000092851692909202919091179091557f0100000000000000000000000000000000000000000000000000000000000000602083811b67ffffffff000000001690921717606681905560655460408051938401919091528201526000906060015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060015b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516114119190612c93565b60405180910390a3505050565b600054610100900460ff166114b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161099e565b610dfc6125db565b60678190556040805160208082018490528251808303909101815290820190915260006112a2565b6114ed610d8f565b67ffffffffffffffff168167ffffffffffffffff16101561156a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b630bebc20067ffffffffffffffff821611156115e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f2068696768604482015260640161099e565b606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260026112a2565b8183556116466107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c0012163828460405160200161168f919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016116bb929190612df6565b600060405180830381600087803b1580156116d557600080fd5b505af11580156116e9573d6000803e3d6000fd5b50505050505050565b6116fa6107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c001216360074660405160200161172a91815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611756929190612df6565b600060405180830381600087803b15801561177057600080fd5b505af1158015610f7a573d6000803e3d6000fd5b6117b26107e560017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b600003610dfc57610dfc6117e760017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b439055565b73ffffffffffffffffffffffffffffffffffffffff81161580159061183b575073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14155b801561184c575061184a610887565b155b1561082f57601260ff168173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c59190612e4e565b60ff1614611955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f53797374656d436f6e6669673a2062616420646563696d616c73206f6620676160448201527f7320706179696e6720746f6b656e000000000000000000000000000000000000606482015260840161099e565b60006119f08273ffffffffffffffffffffffffffffffffffffffff166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa1580156119a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526119eb9190810190612e6b565b61267b565b90506000611a428373ffffffffffffffffffffffffffffffffffffffff166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa1580156119a5573d6000803e3d6000fd5b9050611a51836012848461271e565b611a596107ee565b6040805173ffffffffffffffffffffffffffffffffffffffff86811660208301526012828401526060820186905260808083018690528351808403909101815260a08301938490527fc001216300000000000000000000000000000000000000000000000000000000909352929092169163c0012163916116bb916000919060a401612df6565b8060a001516fffffffffffffffffffffffffffffffff16816060015163ffffffff161115611b90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f53797374656d436f6e6669673a206d696e206261736520666565206d7573742060448201527f6265206c657373207468616e206d617820626173650000000000000000000000606482015260840161099e565b6001816040015160ff1611611c27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f53797374656d436f6e6669673a2064656e6f6d696e61746f72206d757374206260448201527f65206c6172676572207468616e20310000000000000000000000000000000000606482015260840161099e565b6068546080820151825167ffffffffffffffff90921691611c489190612f36565b63ffffffff161115611cb6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b6000816020015160ff1611611d4d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f53797374656d436f6e6669673a20656c6173746963697479206d756c7469706c60448201527f6965722063616e6e6f7420626520300000000000000000000000000000000000606482015260840161099e565b8051602082015163ffffffff82169160ff90911690611d6d908290612f55565b611d779190612f9f565b63ffffffff1614611e0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f53797374656d436f6e6669673a20707265636973696f6e206c6f73732077697460448201527f6820746172676574207265736f75726365206c696d6974000000000000000000606482015260840161099e565b805160698054602084015160408501516060860151608087015160a09097015163ffffffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009095169490941764010000000060ff94851602177fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000093909216929092027fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffff1617660100000000000091851691909102177fffff0000000000000000000000000000000000000000ffffffffffffffffffff166a010000000000000000000093909416929092027fffff00000000000000000000000000000000ffffffffffffffffffffffffffff16929092176e0100000000000000000000000000006fffffffffffffffffffffffffffffffff90921691909102179055565b60008080611f866107e560017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec612d84565b73ffffffffffffffffffffffffffffffffffffffff81169350905082611fc5575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee92601292509050565b60a081901c9150509091565b60606000611fdd611f54565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff82160161205657505060408051808201909152600381527f4554480000000000000000000000000000000000000000000000000000000000602082015290565b61208c6120876107e560017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764612d84565b6127ea565b91505090565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b7fff000000000000000000000000000000000000000000000000000000000000008116156121b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f53797374656d436f6e6669673a207363616c61722065786365656473206d617860448201527f2e00000000000000000000000000000000000000000000000000000000000000606482015260840161099e565b6065829055606681905560408051602081018490529081018290526000906060016113ad565b60018460098111156121f3576121f3612dc7565b14806122105750600284600981111561220e5761220e612dc7565b145b8061222c5750600384600981111561222a5761222a612dc7565b145b6122b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f53797374656d436f6e6669673a20436f6e66696754797065206973206973206e60448201527f6f74206120466565205661756c7420436f6e6669672074797065000000000000606482015260840161099e565b6122c06107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c0012163856122e786868661281e565b6040516020016122f991815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401612325929190612df6565b600060405180830381600087803b15801561233f57600080fd5b505af1158015610d17573d6000803e3d6000fd5b60018263ffffffff1610156123ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53797374656d436f6e6669673a2064656e6f6d696e61746f72206d757374206260448201527f65203e3d20310000000000000000000000000000000000000000000000000000606482015260840161099e565b60018163ffffffff161015612481576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53797374656d436f6e6669673a20656c6173746963697479206d75737420626560448201527f203e3d2031000000000000000000000000000000000000000000000000000000606482015260840161099e565b606a805463ffffffff83811664010000000081027fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090931691861691909117919091179091556040516000916124ee91602086811b67ffffffff0000000016909217910190815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060046113e0565b60606000612531611f54565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016125aa57505060408051808201909152600581527f4574686572000000000000000000000000000000000000000000000000000000602082015290565b61208c6120876107e560017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d612d84565b600054610100900460ff16612672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161099e565b610dfc33612092565b600060208251111561270f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f476173506179696e67546f6b656e3a20737472696e672063616e6e6f7420626560448201527f2067726561746572207468616e20333220627974657300000000000000000000606482015260840161099e565b612718826128a1565b92915050565b61278461274c60017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec612d84565b74ff000000000000000000000000000000000000000060a086901b1673ffffffffffffffffffffffffffffffffffffffff8716179055565b6127b76127b260017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d612d84565b839055565b610f7a6127e560017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764612d84565b829055565b60405160005b82811a15612800576001016127f0565b80825260208201838152600082820152505060408101604052919050565b60006affffffffffffffffffffff831115612865576040517fc4bd89a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1660a084901b60f884600181111561289557612895612dc7565b901b1717949350505050565b8051602181106128b95763ec92f9a36000526004601cfd5b9081015160209190910360031b1b90565b803573ffffffffffffffffffffffffffffffffffffffff811681146128ee57600080fd5b919050565b60006020828403121561290557600080fd5b61290e826128ca565b9392505050565b803563ffffffff811681146128ee57600080fd5b6000806040838503121561293c57600080fd5b61294583612915565b915061295360208401612915565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff811182821017156129ae576129ae61295c565b60405290565b803567ffffffffffffffff811681146128ee57600080fd5b60ff8116811461082f57600080fd5b600060c082840312156129ed57600080fd5b60405160c0810181811067ffffffffffffffff82111715612a1057612a1061295c565b604052905080612a1f83612915565b81526020830135612a2f816129cc565b60208201526040830135612a42816129cc565b6040820152612a5360608401612915565b6060820152612a6460808401612915565b608082015260a08301356fffffffffffffffffffffffffffffffff81168114612a8c57600080fd5b60a0919091015292915050565b600060e08284031215612aab57600080fd5b60405160e0810181811067ffffffffffffffff82111715612ace57612ace61295c565b604052905080612add836128ca565b8152612aeb602084016128ca565b6020820152612afc604084016128ca565b6040820152612b0d606084016128ca565b6060820152612b1e608084016128ca565b6080820152612b2f60a084016128ca565b60a0820152612b4060c084016128ca565b60c08201525092915050565b60008060008060008060008789036102a0811215612b6957600080fd5b6080811215612b7757600080fd5b50612b8061298b565b612b89896128ca565b8152612b9760208a016128ca565b6020820152612ba860408a016128ca565b6040820152606089810135908201529650612bc560808901612915565b9550612bd360a08901612915565b9450612be160c089016129b4565b9350612bf08960e08a016129db565b9250612bff6101a089016128ca565b9150612c0f896101c08a01612a99565b905092959891949750929550565b60005b83811015612c38578181015183820152602001612c20565b83811115610f7a5750506000910152565b60008151808452612c61816020860160208601612c1d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061290e6020830184612c49565b60008060408385031215612cb957600080fd5b50508035926020909101359150565b600060208284031215612cda57600080fd5b61290e826129b4565b60008060008060808587031215612cf957600080fd5b8435600a8110612d0857600080fd5b9350612d16602086016128ca565b925060408501359150606085013560028110612d3157600080fd5b939692955090935050565b600060208284031215612d4e57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612d9657612d96612d55565b500390565b600067ffffffffffffffff808316818516808303821115612dbe57612dbe612d55565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000600a8410612e2f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b83825260406020830152612e466040830184612c49565b949350505050565b600060208284031215612e6057600080fd5b815161290e816129cc565b600060208284031215612e7d57600080fd5b815167ffffffffffffffff80821115612e9557600080fd5b818401915084601f830112612ea957600080fd5b815181811115612ebb57612ebb61295c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715612f0157612f0161295c565b81604052828152876020848701011115612f1a57600080fd5b612f2b836020830160208801612c1d565b979650505050505050565b600063ffffffff808316818516808303821115612dbe57612dbe612d55565b600063ffffffff80841680612f93577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600063ffffffff80831681851681830481118215151615612fc257612fc2612d55565b0294935050505056fea164736f6c634300080f000a", } // SystemConfigABI is the input ABI used to generate the binding from. // Deprecated: Use SystemConfigMetaData.ABI instead. var SystemConfigABI = SystemConfigMetaData.ABI +// SystemConfigBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use SystemConfigMetaData.Bin instead. +var SystemConfigBin = SystemConfigMetaData.Bin + +// DeploySystemConfig deploys a new Ethereum contract, binding an instance of SystemConfig to it. +func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SystemConfig, error) { + parsed, err := SystemConfigMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SystemConfigBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &SystemConfig{SystemConfigCaller: SystemConfigCaller{contract: contract}, SystemConfigTransactor: SystemConfigTransactor{contract: contract}, SystemConfigFilterer: SystemConfigFilterer{contract: contract}}, nil +} + // SystemConfig is an auto generated Go binding around an Ethereum contract. type SystemConfig struct { SystemConfigCaller // Read-only binding to the contract @@ -727,6 +757,37 @@ func (_SystemConfig *SystemConfigCallerSession) Eip1559Elasticity() (uint32, err return _SystemConfig.Contract.Eip1559Elasticity(&_SystemConfig.CallOpts) } +// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. +// +// Solidity: function feeAdmin() view returns(address addr_) +func (_SystemConfig *SystemConfigCaller) FeeAdmin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _SystemConfig.contract.Call(opts, &out, "feeAdmin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. +// +// Solidity: function feeAdmin() view returns(address addr_) +func (_SystemConfig *SystemConfigSession) FeeAdmin() (common.Address, error) { + return _SystemConfig.Contract.FeeAdmin(&_SystemConfig.CallOpts) +} + +// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. +// +// Solidity: function feeAdmin() view returns(address addr_) +func (_SystemConfig *SystemConfigCallerSession) FeeAdmin() (common.Address, error) { + return _SystemConfig.Contract.FeeAdmin(&_SystemConfig.CallOpts) +} + // GasLimit is a free data retrieval call binding the contract method 0xf68016b7. // // Solidity: function gasLimit() view returns(uint64) @@ -1330,25 +1391,25 @@ func (_SystemConfig *SystemConfigCallerSession) Version() (string, error) { return _SystemConfig.Contract.Version(&_SystemConfig.CallOpts) } -// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. +// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. // -// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.contract.Transact(opts, "initialize", _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) +// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.contract.Transact(opts, "initialize", _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) } -// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. +// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. // -// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigSession) Initialize(_owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) +// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigSession) Initialize(_roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) } -// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. +// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. // -// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigTransactorSession) Initialize(_owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) +// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigTransactorSession) Initialize(_roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) } // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. @@ -1414,6 +1475,27 @@ func (_SystemConfig *SystemConfigTransactorSession) SetEIP1559Params(_denominato return _SystemConfig.Contract.SetEIP1559Params(&_SystemConfig.TransactOpts, _denominator, _elasticity) } +// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. +// +// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() +func (_SystemConfig *SystemConfigTransactor) SetFeeVaultConfig(opts *bind.TransactOpts, _type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { + return _SystemConfig.contract.Transact(opts, "setFeeVaultConfig", _type, _recipient, _min, _network) +} + +// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. +// +// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() +func (_SystemConfig *SystemConfigSession) SetFeeVaultConfig(_type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { + return _SystemConfig.Contract.SetFeeVaultConfig(&_SystemConfig.TransactOpts, _type, _recipient, _min, _network) +} + +// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. +// +// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() +func (_SystemConfig *SystemConfigTransactorSession) SetFeeVaultConfig(_type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { + return _SystemConfig.Contract.SetFeeVaultConfig(&_SystemConfig.TransactOpts, _type, _recipient, _min, _network) +} + // SetGasConfig is a paid mutator transaction binding the contract method 0x935f029e. // // Solidity: function setGasConfig(uint256 _overhead, uint256 _scalar) returns() diff --git a/op-e2e/system/gastoken/gastoken_test.go b/op-e2e/system/gastoken/gastoken_test.go index 7e03b19d3930..d83592f28b01 100644 --- a/op-e2e/system/gastoken/gastoken_test.go +++ b/op-e2e/system/gastoken/gastoken_test.go @@ -159,18 +159,21 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System require.NoError(t, err) // Get existing parameters from SystemConfigProxy contract - owner, err := systemConfig.Owner(&bind.CallOpts{}) + roles := bindings.SystemConfigRoles{} + roles.Owner, err = systemConfig.Owner(&bind.CallOpts{}) + require.NoError(t, err) + roles.FeeAdmin, err = systemConfig.FeeAdmin(&bind.CallOpts{}) + require.NoError(t, err) + roles.UnsafeBlockSigner, err = systemConfig.UnsafeBlockSigner(&bind.CallOpts{}) + require.NoError(t, err) + roles.BatcherHash, err = systemConfig.BatcherHash(&bind.CallOpts{}) require.NoError(t, err) basefeeScalar, err := systemConfig.BasefeeScalar(&bind.CallOpts{}) require.NoError(t, err) blobbasefeeScalar, err := systemConfig.BlobbasefeeScalar(&bind.CallOpts{}) require.NoError(t, err) - batcherHash, err := systemConfig.BatcherHash(&bind.CallOpts{}) - require.NoError(t, err) gasLimit, err := systemConfig.GasLimit(&bind.CallOpts{}) require.NoError(t, err) - unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(&bind.CallOpts{}) - require.NoError(t, err) resourceConfig, err := systemConfig.ResourceConfig(&bind.CallOpts{}) require.NoError(t, err) batchInbox, err := systemConfig.BatchInbox(&bind.CallOpts{}) @@ -228,12 +231,10 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System waitForTx(t, tx, err, l1Client) // Reinitialise with existing initializer values but with custom gas token set - tx, err = systemConfig.Initialize(deployerOpts, owner, + tx, err = systemConfig.Initialize(deployerOpts, roles, basefeeScalar, blobbasefeeScalar, - batcherHash, gasLimit, - unsafeBlockSigner, resourceConfig, batchInbox, addresses) diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/.gas-snapshot index 1b1a01905ba5..39eba667c79e 100644 --- a/packages/contracts-bedrock/.gas-snapshot +++ b/packages/contracts-bedrock/.gas-snapshot @@ -4,14 +4,14 @@ GasBenchMark_L1BlockInterop_SetValuesInterop:test_setL1BlockValuesInterop_benchm GasBenchMark_L1BlockInterop_SetValuesInterop_Warm:test_setL1BlockValuesInterop_benchmark() (gas: 5099) GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (gas: 158531) GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7597) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369280) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967420) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564398) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076613) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467098) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512802) -GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72664) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 367239) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2965379) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 560245) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4072288) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 462746) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3508450) +GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 70492) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973) -GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68422) -GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68986) -GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155610) \ No newline at end of file +GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68366) +GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68930) +GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155655) \ No newline at end of file diff --git a/packages/contracts-bedrock/deploy-config/devnetL1-template.json b/packages/contracts-bedrock/deploy-config/devnetL1-template.json index 2f275f46c4f6..bec9c6631793 100644 --- a/packages/contracts-bedrock/deploy-config/devnetL1-template.json +++ b/packages/contracts-bedrock/deploy-config/devnetL1-template.json @@ -27,6 +27,7 @@ "sequencerFeeVaultWithdrawalNetwork": 0, "proxyAdminOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "finalSystemOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", + "systemConfigFeeAdmin": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "superchainConfigGuardian": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "finalizationPeriodSeconds": 2, "fundDevAccounts": true, diff --git a/packages/contracts-bedrock/deploy-config/hardhat.json b/packages/contracts-bedrock/deploy-config/hardhat.json index 965d403d4d82..9009f9157d3e 100644 --- a/packages/contracts-bedrock/deploy-config/hardhat.json +++ b/packages/contracts-bedrock/deploy-config/hardhat.json @@ -1,5 +1,6 @@ { "finalSystemOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "systemConfigFeeAdmin": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", "superchainConfigGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "l1StartingBlockTag": "earliest", diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 59b16410f3a4..78c932ac7eea 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -76,7 +76,9 @@ runs = 256 depth = 32 [profile.ciheavy] -fuzz = { runs = 20000 } +# temp reduce fuzz runs for 1 pr +# fuzz = { runs = 20000 } +fuzz = { runs = 200 } [profile.ciheavy.invariant] runs = 128 diff --git a/packages/contracts-bedrock/scripts/Artifacts.s.sol b/packages/contracts-bedrock/scripts/Artifacts.s.sol index 3ed021ea9dbb..c516d2cf55e9 100644 --- a/packages/contracts-bedrock/scripts/Artifacts.s.sol +++ b/packages/contracts-bedrock/scripts/Artifacts.s.sol @@ -47,7 +47,6 @@ abstract contract Artifacts { /// @notice Setup function. The arguments here function setUp() public virtual { deploymentOutfile = Config.deploymentOutfile(); - console.log("Writing artifact to %s", deploymentOutfile); ForgeArtifacts.ensurePath(deploymentOutfile); uint256 chainId = Config.chainID(); @@ -183,7 +182,6 @@ abstract contract Artifacts { /// @param _name The name of the deployment. /// @param _deployed The address of the deployment. function save(string memory _name, address _deployed) public { - console.log("Saving %s: %s", _name, _deployed); if (bytes(_name).length == 0) { revert InvalidDeployment("EmptyName"); } diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 207c59732146..098cb5e390f4 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -8,27 +8,28 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; // Scripts import { Deployer } from "scripts/deploy/Deployer.sol"; import { Config, OutputMode, OutputModeUtils, Fork, ForkUtils, LATEST_FORK } from "scripts/libraries/Config.sol"; +import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { Process } from "scripts/libraries/Process.sol"; import { SetPreinstalls } from "scripts/SetPreinstalls.s.sol"; + +// Contracts +import { SequencerFeeVault } from "src/L2/SequencerFeeVault.sol"; +import { BaseFeeVault } from "src/L2/BaseFeeVault.sol"; +import { L1FeeVault } from "src/L2/L1FeeVault.sol"; +import { OptimismSuperchainERC20Beacon } from "src/L2/OptimismSuperchainERC20Beacon.sol"; +import { OptimismMintableERC721Factory } from "src/L2/OptimismMintableERC721Factory.sol"; +import { GovernanceToken } from "src/governance/GovernanceToken.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; +import { Constants } from "src/libraries/Constants.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; import { Types } from "src/libraries/Types.sol"; // Interfaces -import { ISequencerFeeVault } from "src/L2/interfaces/ISequencerFeeVault.sol"; -import { IBaseFeeVault } from "src/L2/interfaces/IBaseFeeVault.sol"; -import { IL1FeeVault } from "src/L2/interfaces/IL1FeeVault.sol"; -import { IOptimismMintableERC721Factory } from "src/universal/interfaces/IOptimismMintableERC721Factory.sol"; import { IGovernanceToken } from "src/governance/interfaces/IGovernanceToken.sol"; -import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; -import { IL2StandardBridge } from "src/L2/interfaces/IL2StandardBridge.sol"; -import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMessenger.sol"; import { IGasPriceOracle } from "src/L2/interfaces/IGasPriceOracle.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; @@ -110,7 +111,12 @@ contract L2Genesis is Deployer { /// Sets the precompiles, proxies, and the implementation accounts to be `vm.dumpState` /// to generate a L2 genesis alloc. function runWithStateDump() public { - runWithOptions(Config.outputMode(), cfg.fork(), artifactDependencies()); + runWithOptions({ + _mode: Config.outputMode(), + _fork: Config.fork(), + _populateNetworkConfig: true, + _l1Dependencies: artifactDependencies() + }); } /// @notice Alias for `runWithStateDump` so that no `--sig` needs to be specified. @@ -120,45 +126,72 @@ contract L2Genesis is Deployer { /// @notice This is used by op-e2e to have a version of the L2 allocs for each upgrade. function runWithAllUpgrades() public { - runWithOptions(OutputMode.ALL, LATEST_FORK, artifactDependencies()); + console.log("L2Genesis: runWithAllUpgrades"); + runWithOptions({ + _mode: OutputMode.ALL, + _fork: LATEST_FORK, + _populateNetworkConfig: true, + _l1Dependencies: artifactDependencies() + }); } /// @notice This is used by new experimental interop deploy tooling. function runWithEnv() public { // The setUp() is skipped (since we insert a custom DeployConfig, and do not use Artifacts) deployer = makeAddr("deployer"); - runWithOptions( - OutputMode.NONE, - Config.fork(), - L1Dependencies({ + runWithOptions({ + _mode: OutputMode.NONE, + _fork: Config.fork(), + _populateNetworkConfig: false, + _l1Dependencies: L1Dependencies({ l1CrossDomainMessengerProxy: payable(vm.envAddress("L2GENESIS_L1CrossDomainMessengerProxy")), l1StandardBridgeProxy: payable(vm.envAddress("L2GENESIS_L1StandardBridgeProxy")), l1ERC721BridgeProxy: payable(vm.envAddress("L2GENESIS_L1ERC721BridgeProxy")) }) - ); + }); } /// @notice This is used by foundry tests to enable the latest fork with the /// given L1 dependencies. function runWithLatestLocal(L1Dependencies memory _l1Dependencies) public { - runWithOptions(OutputMode.NONE, LATEST_FORK, _l1Dependencies); + runWithOptions({ + _mode: OutputMode.NONE, + _fork: LATEST_FORK, + _populateNetworkConfig: true, + _l1Dependencies: _l1Dependencies + }); } /// @notice Build the L2 genesis. - function runWithOptions(OutputMode _mode, Fork _fork, L1Dependencies memory _l1Dependencies) public { + /// @param _mode The mode to run the script in. + /// @param _fork The fork to build the genesis for. + /// @param _populateNetworkConfig If true, the L1 Block contract will be populated with network specific + /// configuration. Otherwise, the standard genesis will be built. + function runWithOptions( + OutputMode _mode, + Fork _fork, + bool _populateNetworkConfig, + L1Dependencies memory _l1Dependencies + ) + public + { console.log("L2Genesis: outputMode: %s, fork: %s", _mode.toString(), _fork.toString()); vm.startPrank(deployer); vm.chainId(cfg.l2ChainID()); dealEthToPrecompiles(); setPredeployProxies(); - setPredeployImplementations(_l1Dependencies); + setPredeployImplementations(); setPreinstalls(); if (cfg.fundDevAccounts()) { fundDevAccounts(); } vm.stopPrank(); + if (_populateNetworkConfig) { + _setNetworkConfig(_l1Dependencies, cfg); + } + if (writeForkGenesisAllocs(_fork, Fork.DELTA, _mode)) { return; } @@ -182,6 +215,59 @@ contract L2Genesis is Deployer { if (writeForkGenesisAllocs(_fork, Fork.HOLOCENE, _mode)) { return; } + + if (writeForkGenesisAllocs(_fork, Fork.ISTHMUS, _mode)) { + return; + } + } + + /// @notice Sets network-specific configuration in the L1Block contract + /// @param _l1Dependencies The L1 contract dependencies needed for configuration + function _setNetworkConfig(L1Dependencies memory _l1Dependencies, DeployConfig _config) internal { + console.log("L2Genesis: Modify the standard L2 genesis with network specific configuration"); + vm.startPrank(Constants.DEPOSITOR_ACCOUNT); + + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS, abi.encode(_l1Dependencies.l1ERC721BridgeProxy) + ); + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS, abi.encode(_l1Dependencies.l1CrossDomainMessengerProxy) + ); + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS, abi.encode(_l1Dependencies.l1StandardBridgeProxy) + ); + + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.REMOTE_CHAIN_ID, abi.encode(_config.l1ChainID()) + ); + + bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: _config.sequencerFeeVaultRecipient(), + _amount: _config.sequencerFeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(_config.sequencerFeeVaultWithdrawalNetwork()) + }); + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig) + ); + + bytes32 baseFeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: _config.baseFeeVaultRecipient(), + _amount: _config.baseFeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(_config.baseFeeVaultWithdrawalNetwork()) + }); + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.BASE_FEE_VAULT_CONFIG, abi.encode(baseFeeVaultConfig) + ); + + bytes32 l1FeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: _config.l1FeeVaultRecipient(), + _amount: _config.l1FeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(_config.l1FeeVaultWithdrawalNetwork()) + }); + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( + Types.ConfigType.L1_FEE_VAULT_CONFIG, abi.encode(l1FeeVaultConfig) + ); + vm.stopPrank(); } function writeForkGenesisAllocs(Fork _latest, Fork _current, OutputMode _mode) internal returns (bool isLatest_) { @@ -241,24 +327,20 @@ contract L2Genesis is Deployer { /// @notice Sets all the implementations for the predeploy proxies. For contracts without proxies, /// sets the deployed bytecode at their expected predeploy address. /// LEGACY_ERC20_ETH and L1_MESSAGE_SENDER are deprecated and are not set. - function setPredeployImplementations(L1Dependencies memory _l1Dependencies) internal { - console.log("Setting predeploy implementations with L1 contract dependencies:"); - console.log("- L1CrossDomainMessengerProxy: %s", _l1Dependencies.l1CrossDomainMessengerProxy); - console.log("- L1StandardBridgeProxy: %s", _l1Dependencies.l1StandardBridgeProxy); - console.log("- L1ERC721BridgeProxy: %s", _l1Dependencies.l1ERC721BridgeProxy); + function setPredeployImplementations() internal { setLegacyMessagePasser(); // 0 // 01: legacy, not used in OP-Stack setDeployerWhitelist(); // 2 // 3,4,5: legacy, not used in OP-Stack. setWETH(); // 6: WETH (not behind a proxy) - setL2CrossDomainMessenger(_l1Dependencies.l1CrossDomainMessengerProxy); // 7 + setL2CrossDomainMessenger(); // 7 // 8,9,A,B,C,D,E: legacy, not used in OP-Stack. setGasPriceOracle(); // f - setL2StandardBridge(_l1Dependencies.l1StandardBridgeProxy); // 10 + setL2StandardBridge(); // 10 setSequencerFeeVault(); // 11 setOptimismMintableERC20Factory(); // 12 setL1BlockNumber(); // 13 - setL2ERC721Bridge(_l1Dependencies.l1ERC721BridgeProxy); // 14 + setL2ERC721Bridge(); // 14 setL1Block(); // 15 setL2ToL1MessagePasser(); // 16 setOptimismMintableERC721Factory(); // 17 @@ -284,12 +366,9 @@ contract L2Genesis is Deployer { // Note the ProxyAdmin implementation itself is behind a proxy that owns itself. address impl = _setImplementationCode(Predeploys.PROXY_ADMIN); - bytes32 _ownerSlot = bytes32(0); - - // there is no initialize() function, so we just set the storage manually. - vm.store(Predeploys.PROXY_ADMIN, _ownerSlot, bytes32(uint256(uint160(cfg.proxyAdminOwner())))); // update the proxy to not be uninitialized (although not standard initialize pattern) - vm.store(impl, _ownerSlot, bytes32(uint256(uint160(cfg.proxyAdminOwner())))); + bytes32 _ownerSlot = bytes32(0); + vm.store(impl, _ownerSlot, bytes32(uint256(0xdead))); } function setL2ToL1MessagePasser() public { @@ -297,102 +376,40 @@ contract L2Genesis is Deployer { } /// @notice This predeploy is following the safety invariant #1. - function setL2CrossDomainMessenger(address payable _l1CrossDomainMessengerProxy) public { - address impl = _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER); - - IL2CrossDomainMessenger(impl).initialize({ _l1CrossDomainMessenger: ICrossDomainMessenger(address(0)) }); - - IL2CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).initialize({ - _l1CrossDomainMessenger: ICrossDomainMessenger(_l1CrossDomainMessengerProxy) - }); + function setL2CrossDomainMessenger() public { + _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER); } /// @notice This predeploy is following the safety invariant #1. - function setL2StandardBridge(address payable _l1StandardBridgeProxy) public { - address impl; + function setL2StandardBridge() public { if (cfg.useInterop()) { string memory cname = "L2StandardBridgeInterop"; - impl = Predeploys.predeployToCodeNamespace(Predeploys.L2_STANDARD_BRIDGE); + address impl = Predeploys.predeployToCodeNamespace(Predeploys.L2_STANDARD_BRIDGE); console.log("Setting %s implementation at: %s", cname, impl); vm.etch(impl, vm.getDeployedCode(string.concat(cname, ".sol:", cname))); } else { - impl = _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE); + _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE); } - - IL2StandardBridge(payable(impl)).initialize({ _otherBridge: IStandardBridge(payable(address(0))) }); - - IL2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).initialize({ - _otherBridge: IStandardBridge(_l1StandardBridgeProxy) - }); } /// @notice This predeploy is following the safety invariant #1. - function setL2ERC721Bridge(address payable _l1ERC721BridgeProxy) public { - address impl = _setImplementationCode(Predeploys.L2_ERC721_BRIDGE); - - IL2ERC721Bridge(impl).initialize({ _l1ERC721Bridge: payable(address(0)) }); - - IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE).initialize({ _l1ERC721Bridge: payable(_l1ERC721BridgeProxy) }); + function setL2ERC721Bridge() public { + _setImplementationCode(Predeploys.L2_ERC721_BRIDGE); } /// @notice This predeploy is following the safety invariant #2, function setSequencerFeeVault() public { - ISequencerFeeVault vault = ISequencerFeeVault( - DeployUtils.create1( - "SequencerFeeVault", - DeployUtils.encodeConstructor( - abi.encodeCall( - ISequencerFeeVault.__constructor__, - ( - cfg.sequencerFeeVaultRecipient(), - cfg.sequencerFeeVaultMinimumWithdrawalAmount(), - Types.WithdrawalNetwork(cfg.sequencerFeeVaultWithdrawalNetwork()) - ) - ) - ) - ) - ); - - address impl = Predeploys.predeployToCodeNamespace(Predeploys.SEQUENCER_FEE_WALLET); - console.log("Setting %s implementation at: %s", "SequencerFeeVault", impl); - vm.etch(impl, address(vault).code); - - /// Reset so its not included state dump - vm.etch(address(vault), ""); - vm.resetNonce(address(vault)); + _setImplementationCode(Predeploys.SEQUENCER_FEE_WALLET); } /// @notice This predeploy is following the safety invariant #1. function setOptimismMintableERC20Factory() public { - address impl = _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); - - IOptimismMintableERC20Factory(impl).initialize({ _bridge: address(0) }); - - IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).initialize({ - _bridge: Predeploys.L2_STANDARD_BRIDGE - }); + _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); } /// @notice This predeploy is following the safety invariant #2, function setOptimismMintableERC721Factory() public { - IOptimismMintableERC721Factory factory = IOptimismMintableERC721Factory( - DeployUtils.create1( - "OptimismMintableERC721Factory", - DeployUtils.encodeConstructor( - abi.encodeCall( - IOptimismMintableERC721Factory.__constructor__, (Predeploys.L2_ERC721_BRIDGE, cfg.l1ChainID()) - ) - ) - ) - ); - - address impl = Predeploys.predeployToCodeNamespace(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); - console.log("Setting %s implementation at: %s", "OptimismMintableERC721Factory", impl); - vm.etch(impl, address(factory).code); - - /// Reset so its not included state dump - vm.etch(address(factory), ""); - vm.resetNonce(address(factory)); + _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); } /// @notice This predeploy is following the safety invariant #1. @@ -439,56 +456,12 @@ contract L2Genesis is Deployer { /// @notice This predeploy is following the safety invariant #2. function setBaseFeeVault() public { - IBaseFeeVault vault = IBaseFeeVault( - DeployUtils.create1( - "BaseFeeVault", - DeployUtils.encodeConstructor( - abi.encodeCall( - IBaseFeeVault.__constructor__, - ( - cfg.baseFeeVaultRecipient(), - cfg.baseFeeVaultMinimumWithdrawalAmount(), - Types.WithdrawalNetwork(cfg.baseFeeVaultWithdrawalNetwork()) - ) - ) - ) - ) - ); - - address impl = Predeploys.predeployToCodeNamespace(Predeploys.BASE_FEE_VAULT); - console.log("Setting %s implementation at: %s", "BaseFeeVault", impl); - vm.etch(impl, address(vault).code); - - /// Reset so its not included state dump - vm.etch(address(vault), ""); - vm.resetNonce(address(vault)); + _setImplementationCode(Predeploys.BASE_FEE_VAULT); } /// @notice This predeploy is following the safety invariant #2. function setL1FeeVault() public { - IL1FeeVault vault = IL1FeeVault( - DeployUtils.create1( - "L1FeeVault", - DeployUtils.encodeConstructor( - abi.encodeCall( - IL1FeeVault.__constructor__, - ( - cfg.l1FeeVaultRecipient(), - cfg.l1FeeVaultMinimumWithdrawalAmount(), - Types.WithdrawalNetwork(cfg.l1FeeVaultWithdrawalNetwork()) - ) - ) - ) - ) - ); - - address impl = Predeploys.predeployToCodeNamespace(Predeploys.L1_FEE_VAULT); - console.log("Setting %s implementation at: %s", "L1FeeVault", impl); - vm.etch(impl, address(vault).code); - - /// Reset so its not included state dump - vm.etch(address(vault), ""); - vm.resetNonce(address(vault)); + _setImplementationCode(Predeploys.L1_FEE_VAULT); } /// @notice This predeploy is following the safety invariant #2. diff --git a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol index 25d67be3828c..e176a1d7c53e 100644 --- a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol +++ b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol @@ -13,6 +13,7 @@ import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Types } from "scripts/libraries/Types.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -40,8 +41,7 @@ library ChainAssertions { function postDeployAssertions( Types.ContractSet memory _prox, DeployConfig _cfg, - uint256 _l2OutputOracleStartingTimestamp, - Vm _vm + uint256 _l2OutputOracleStartingTimestamp ) internal view @@ -52,7 +52,7 @@ library ChainAssertions { require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt))); checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); - checkL1CrossDomainMessenger({ _contracts: _prox, _vm: _vm, _isProxy: true }); + checkL1CrossDomainMessenger({ _contracts: _prox, _isProxy: true }); checkL1StandardBridge({ _contracts: _prox, _isProxy: true }); checkL2OutputOracle({ _contracts: _prox, @@ -77,7 +77,7 @@ library ChainAssertions { ); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(config), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(config), _slot: 0, _offset: 0 }); IResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig(); @@ -109,18 +109,18 @@ library ChainAssertions { require(config.optimismPortal() == _contracts.OptimismPortal, "CHECK-SCFG-200"); require(config.optimismMintableERC20Factory() == _contracts.OptimismMintableERC20Factory, "CHECK-SCFG-210"); } else { - require(config.owner() == address(0xdead), "CHECK-SCFG-220"); + require(config.owner() == address(0), "CHECK-SCFG-220"); require(config.overhead() == 0, "CHECK-SCFG-230"); - require(config.scalar() == uint256(0x01) << 248, "CHECK-SCFG-240"); // version 1 + require(config.scalar() == 0, "CHECK-SCFG-240"); require(config.basefeeScalar() == 0, "CHECK-SCFG-250"); require(config.blobbasefeeScalar() == 0, "CHECK-SCFG-260"); require(config.batcherHash() == bytes32(0), "CHECK-SCFG-270"); - require(config.gasLimit() == 1, "CHECK-SCFG-280"); + require(config.gasLimit() == 0, "CHECK-SCFG-280"); require(config.unsafeBlockSigner() == address(0), "CHECK-SCFG-290"); // Check _config - require(resourceConfig.maxResourceLimit == 1, "CHECK-SCFG-300"); - require(resourceConfig.elasticityMultiplier == 1, "CHECK-SCFG-310"); - require(resourceConfig.baseFeeMaxChangeDenominator == 2, "CHECK-SCFG-320"); + require(resourceConfig.maxResourceLimit == 0, "CHECK-SCFG-300"); + require(resourceConfig.elasticityMultiplier == 0, "CHECK-SCFG-310"); + require(resourceConfig.baseFeeMaxChangeDenominator == 0, "CHECK-SCFG-320"); require(resourceConfig.systemTxMaxGas == 0, "CHECK-SCFG-330"); require(resourceConfig.minimumBaseFee == 0, "CHECK-SCFG-340"); require(resourceConfig.maximumBaseFee == 0, "CHECK-SCFG-350"); @@ -163,7 +163,7 @@ library ChainAssertions { } /// @notice Asserts that the L1CrossDomainMessenger is setup correctly - function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, Vm _vm, bool _isProxy) internal view { + function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, bool _isProxy) internal view { IL1CrossDomainMessenger messenger = IL1CrossDomainMessenger(_contracts.L1CrossDomainMessenger); console.log( "Running chain assertions on the L1CrossDomainMessenger %s at %s", @@ -173,7 +173,7 @@ library ChainAssertions { require(address(messenger) != address(0), "CHECK-L1XDM-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); + DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "CHECK-L1XDM-20"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "CHECK-L1XDM-30"); @@ -182,8 +182,6 @@ library ChainAssertions { require(address(messenger.PORTAL()) == _contracts.OptimismPortal, "CHECK-L1XDM-40"); require(address(messenger.portal()) == _contracts.OptimismPortal, "CHECK-L1XDM-50"); require(address(messenger.superchainConfig()) == _contracts.SuperchainConfig, "CHECK-L1XDM-60"); - bytes32 xdmSenderSlot = _vm.load(address(messenger), bytes32(uint256(204))); - require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "CHECK-L1XDM-70"); } else { require(address(messenger.PORTAL()) == address(0), "CHECK-L1XDM-80"); require(address(messenger.portal()) == address(0), "CHECK-L1XDM-90"); @@ -202,7 +200,7 @@ library ChainAssertions { require(address(bridge) != address(0), "CHECK-L1SB-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); if (_isProxy) { require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger, "CHECK-L1SB-20"); @@ -237,7 +235,7 @@ library ChainAssertions { require(address(factory) != address(0), "CHECK-DG-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); // The same check is made for both proxy and implementation require(factory.owner() == _expectedOwner, "CHECK-DG-20"); @@ -277,7 +275,7 @@ library ChainAssertions { require(address(weth) != address(0), "CHECK-DWETH-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); if (_isProxy) { require(weth.owner() == _expectedOwner, "CHECK-DWETH-20"); @@ -308,7 +306,7 @@ library ChainAssertions { require(address(weth) != address(0), "CHECK-PDWETH-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); if (_isProxy) { require(weth.owner() == _expectedOwner, "CHECK-PDWETH-20"); @@ -339,7 +337,7 @@ library ChainAssertions { require(address(oracle) != address(0), "CHECK-L2OO-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(oracle), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(oracle), _slot: 0, _offset: 0 }); if (_isProxy) { require(oracle.SUBMISSION_INTERVAL() == _cfg.l2OutputOracleSubmissionInterval(), "CHECK-L2OO-20"); @@ -381,7 +379,7 @@ library ChainAssertions { require(address(factory) != address(0), "CHECK-MERC20F-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); if (_isProxy) { require(factory.BRIDGE() == _contracts.L1StandardBridge, "CHECK-MERC20F-10"); @@ -404,7 +402,7 @@ library ChainAssertions { require(address(bridge) != address(0), "CHECK-L1ERC721B-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "CHECK-L1ERC721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "CHECK-L1ERC721B-20"); @@ -431,7 +429,7 @@ library ChainAssertions { require(address(portal) != address(0), "CHECK-OP-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); address guardian = _cfg.superchainConfigGuardian(); if (guardian.code.length == 0) { @@ -471,7 +469,7 @@ library ChainAssertions { require(address(portal) != address(0), "CHECK-OP2-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); address guardian = _cfg.superchainConfigGuardian(); if (guardian.code.length == 0) { @@ -514,7 +512,7 @@ library ChainAssertions { require(address(versions) != address(0), "CHECK-PV-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(versions), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(versions), _slot: 0, _offset: 0 }); if (_isProxy) { require(versions.owner() == _cfg.finalSystemOwner(), "CHECK-PV-20"); @@ -546,7 +544,7 @@ library ChainAssertions { require(address(superchainConfig) != address(0), "CHECK-SC-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 }); if (_isProxy) { require(superchainConfig.guardian() == _cfg.superchainConfigGuardian(), "CHECK-SC-20"); @@ -568,7 +566,7 @@ library ChainAssertions { require(address(opcm) != address(0), "CHECK-OPCM-10"); // Check that the contract is initialized - assertInitializedSlotIsSet({ _contractAddress: address(opcm), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(opcm), _slot: 0, _offset: 0 }); // These values are immutable so are shared by the proxy and implementation require(address(opcm.superchainConfig()) == address(_contracts.SuperchainConfig), "CHECK-OPCM-30"); @@ -576,15 +574,4 @@ library ChainAssertions { // TODO: Add assertions for blueprints and setters? } - - /// @dev Asserts that for a given contract the value of a storage slot at an offset is 1 or 0xff. - /// A call to `initialize` will set it to 1 and a call to _disableInitializers will set it to 0xff. - function assertInitializedSlotIsSet(address _contractAddress, uint256 _slot, uint256 _offset) internal view { - bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot)); - uint8 val = uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF); - require( - val == uint8(1) || val == uint8(0xff), - "ChainAssertions: storage value is not 1 or 0xff at the given slot and offset" - ); - } } diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index d40c03987d53..ad3a53b159d5 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -155,7 +155,7 @@ contract Deploy is Deployer { DelayedWETH: getAddress("DelayedWETHProxy"), PermissionedDelayedWETH: getAddress("PermissionedDelayedWETHProxy"), AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"), - OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"), + OptimismMintableERC20Factory: getAddress("L1OptimismMintableERC20FactoryProxy"), OptimismPortal: getAddress("OptimismPortalProxy"), OptimismPortal2: getAddress("OptimismPortalProxy"), SystemConfig: getAddress("SystemConfigProxy"), @@ -176,7 +176,7 @@ contract Deploy is Deployer { DelayedWETH: getAddress("DelayedWETH"), PermissionedDelayedWETH: getAddress("PermissionedDelayedWETH"), AnchorStateRegistry: getAddress("AnchorStateRegistry"), - OptimismMintableERC20Factory: getAddress("OptimismMintableERC20Factory"), + OptimismMintableERC20Factory: getAddress("L1OptimismMintableERC20Factory"), OptimismPortal: getAddress("OptimismPortal"), OptimismPortal2: getAddress("OptimismPortal2"), SystemConfig: getAddress("SystemConfig"), @@ -398,7 +398,9 @@ contract Deploy is Deployer { } save("L1CrossDomainMessenger", address(dio.l1CrossDomainMessengerImpl())); + // Save under both names for backwards compatibility save("OptimismMintableERC20Factory", address(dio.optimismMintableERC20FactoryImpl())); + save("L1OptimismMintableERC20Factory", address(dio.optimismMintableERC20FactoryImpl())); save("SystemConfig", address(dio.systemConfigImpl())); save("L1StandardBridge", address(dio.l1StandardBridgeImpl())); save("L1ERC721Bridge", address(dio.l1ERC721BridgeImpl())); @@ -413,7 +415,7 @@ contract Deploy is Deployer { save("OPContractsManager", address(dio.opcmImpl())); Types.ContractSet memory contracts = _impls(); - ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _vm: vm, _isProxy: false }); + ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _isProxy: false }); ChainAssertions.checkL1StandardBridge({ _contracts: contracts, _isProxy: false }); ChainAssertions.checkL1ERC721Bridge({ _contracts: contracts, _isProxy: false }); ChainAssertions.checkOptimismPortal2({ _contracts: contracts, _cfg: cfg, _isProxy: false }); @@ -456,7 +458,9 @@ contract Deploy is Deployer { save("AddressManager", address(deployOutput.addressManager)); save("L1ERC721BridgeProxy", address(deployOutput.l1ERC721BridgeProxy)); save("SystemConfigProxy", address(deployOutput.systemConfigProxy)); + // Save under both names for backwards compatibility save("OptimismMintableERC20FactoryProxy", address(deployOutput.optimismMintableERC20FactoryProxy)); + save("L1OptimismMintableERC20FactoryProxy", address(deployOutput.optimismMintableERC20FactoryProxy)); save("L1StandardBridgeProxy", address(deployOutput.l1StandardBridgeProxy)); save("L1CrossDomainMessengerProxy", address(deployOutput.l1CrossDomainMessengerProxy)); @@ -823,12 +827,15 @@ contract Deploy is Deployer { _data: abi.encodeCall( ISystemConfig.initialize, ( - cfg.finalSystemOwner(), + ISystemConfig.Roles({ + owner: cfg.finalSystemOwner(), + feeAdmin: cfg.systemConfigFeeAdmin(), + unsafeBlockSigner: cfg.p2pSequencerAddress(), + batcherHash: batcherHash + }), cfg.basefeeScalar(), cfg.blobbasefeeScalar(), - batcherHash, uint64(cfg.l2GenesisBlockGasLimit()), - cfg.p2pSequencerAddress(), Constants.DEFAULT_RESOURCE_CONFIG(), cfg.batchInboxAddress(), ISystemConfig.Addresses({ @@ -837,7 +844,7 @@ contract Deploy is Deployer { l1StandardBridge: mustGetAddress("L1StandardBridgeProxy"), disputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"), optimismPortal: mustGetAddress("OptimismPortalProxy"), - optimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"), + optimismMintableERC20Factory: mustGetAddress("L1OptimismMintableERC20FactoryProxy"), gasPayingToken: customGasTokenAddress }) ) @@ -1264,7 +1271,8 @@ contract Deploy is Deployer { batcher: cfg.batchSenderAddress(), unsafeBlockSigner: cfg.p2pSequencerAddress(), proposer: cfg.l2OutputOracleProposer(), - challenger: cfg.l2OutputOracleChallenger() + challenger: cfg.l2OutputOracleChallenger(), + systemConfigFeeAdmin: cfg.systemConfigFeeAdmin() }), basefeeScalar: cfg.basefeeScalar(), blobBasefeeScalar: cfg.blobbasefeeScalar(), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index 69341dd3e874..a95346f093b5 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -86,6 +86,7 @@ contract DeployConfig is Script { uint256 public daResolveWindow; uint256 public daBondSize; uint256 public daResolverRefundPercentage; + address public systemConfigFeeAdmin; bool public useCustomGasToken; address public customGasTokenAddress; @@ -101,6 +102,7 @@ contract DeployConfig is Script { } finalSystemOwner = stdJson.readAddress(_json, "$.finalSystemOwner"); + systemConfigFeeAdmin = stdJson.readAddress(_json, "$.systemConfigFeeAdmin"); superchainConfigGuardian = stdJson.readAddress(_json, "$.superchainConfigGuardian"); l1ChainID = stdJson.readUint(_json, "$.l1ChainID"); l2ChainID = stdJson.readUint(_json, "$.l2ChainID"); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index c9048a07dfa3..40f9b79c5580 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -29,7 +29,8 @@ import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMessenger.sol"; import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; -import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; +import { IL1OptimismMintableERC20Factory as IOptimismMintableERC20Factory } from + "src/L1/interfaces/IL1OptimismMintableERC20Factory.sol"; import { OPContractsManagerInterop } from "src/L1/OPContractsManagerInterop.sol"; import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; @@ -370,19 +371,19 @@ contract DeployImplementationsOutput is BaseDeployIO { DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 }); - require(systemConfig.owner() == address(0xdead), "SYSCON-10"); + require(systemConfig.owner() == address(0), "SYSCON-10"); require(systemConfig.overhead() == 0, "SYSCON-20"); - require(systemConfig.scalar() == uint256(0x01) << 248, "SYSCON-30"); + require(systemConfig.scalar() == 0, "SYSCON-30"); require(systemConfig.basefeeScalar() == 0, "SYSCON-40"); require(systemConfig.blobbasefeeScalar() == 0, "SYSCON-50"); require(systemConfig.batcherHash() == bytes32(0), "SYSCON-60"); - require(systemConfig.gasLimit() == 1, "SYSCON-70"); + require(systemConfig.gasLimit() == 0, "SYSCON-70"); require(systemConfig.unsafeBlockSigner() == address(0), "SYSCON-80"); IResourceMetering.ResourceConfig memory resourceConfig = systemConfig.resourceConfig(); - require(resourceConfig.maxResourceLimit == 1, "SYSCON-90"); - require(resourceConfig.elasticityMultiplier == 1, "SYSCON-100"); - require(resourceConfig.baseFeeMaxChangeDenominator == 2, "SYSCON-110"); + require(resourceConfig.maxResourceLimit == 0, "SYSCON-90"); + require(resourceConfig.elasticityMultiplier == 0, "SYSCON-100"); + require(resourceConfig.baseFeeMaxChangeDenominator == 0, "SYSCON-110"); require(resourceConfig.systemTxMaxGas == 0, "SYSCON-120"); require(resourceConfig.minimumBaseFee == 0, "SYSCON-130"); require(resourceConfig.maximumBaseFee == 0, "SYSCON-140"); @@ -400,7 +401,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidL1CrossDomainMessengerImpl(DeployImplementationsInput) internal view { IL1CrossDomainMessenger messenger = l1CrossDomainMessengerImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); + DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-10"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-20"); @@ -408,14 +409,15 @@ contract DeployImplementationsOutput is BaseDeployIO { require(address(messenger.portal()) == address(0), "L1xDM-40"); require(address(messenger.superchainConfig()) == address(0), "L1xDM-50"); - bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204))); - require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60"); + // TODO: vm.expectRevert is not supported by op-chain-ops, so we can't check this yet. + // vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); + // messenger.xDomainMessageSender(); } function assertValidL1ERC721BridgeImpl(DeployImplementationsInput) internal view { IL1ERC721Bridge bridge = l1ERC721BridgeImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20"); @@ -427,7 +429,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidL1StandardBridgeImpl(DeployImplementationsInput) internal view { IL1StandardBridge bridge = l1StandardBridgeImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); require(address(bridge.MESSENGER()) == address(0), "L1SB-10"); require(address(bridge.messenger()) == address(0), "L1SB-20"); @@ -439,7 +441,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidOptimismMintableERC20FactoryImpl(DeployImplementationsInput) internal view { IOptimismMintableERC20Factory factory = optimismMintableERC20FactoryImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); require(address(factory.BRIDGE()) == address(0), "MERC20F-10"); require(address(factory.bridge()) == address(0), "MERC20F-20"); @@ -788,7 +790,7 @@ contract DeployImplementations is Script { vm.broadcast(msg.sender); impl = IOptimismMintableERC20Factory( DeployUtils.create1({ - _name: "OptimismMintableERC20Factory", + _name: "L1OptimismMintableERC20Factory", _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismMintableERC20Factory.__constructor__, ())) }) ); @@ -796,7 +798,7 @@ contract DeployImplementations is Script { revert(string.concat("DeployImplementations: failed to deploy release ", release)); } - vm.label(address(impl), "OptimismMintableERC20FactoryImpl"); + vm.label(address(impl), "L1OptimismMintableERC20FactoryImpl"); _dio.set(_dio.optimismMintableERC20FactoryImpl.selector, address(impl)); } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 7a5d1f5cc6b2..247a0575f009 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -38,6 +38,7 @@ import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimis contract DeployOPChainInput is BaseDeployIO { address internal _opChainProxyAdminOwner; address internal _systemConfigOwner; + address internal _systemConfigFeeAdmin; address internal _batcher; address internal _unsafeBlockSigner; address internal _proposer; @@ -64,6 +65,7 @@ contract DeployOPChainInput is BaseDeployIO { require(_addr != address(0), "DeployOPChainInput: cannot set zero address"); if (_sel == this.opChainProxyAdminOwner.selector) _opChainProxyAdminOwner = _addr; else if (_sel == this.systemConfigOwner.selector) _systemConfigOwner = _addr; + else if (_sel == this.systemConfigFeeAdmin.selector) _systemConfigFeeAdmin = _addr; else if (_sel == this.batcher.selector) _batcher = _addr; else if (_sel == this.unsafeBlockSigner.selector) _unsafeBlockSigner = _addr; else if (_sel == this.proposer.selector) _proposer = _addr; @@ -123,6 +125,11 @@ contract DeployOPChainInput is BaseDeployIO { return _systemConfigOwner; } + function systemConfigFeeAdmin() public view returns (address) { + require(_systemConfigFeeAdmin != address(0), "DeployOPChainInput: not set"); + return _systemConfigFeeAdmin; + } + function batcher() public view returns (address) { require(_batcher != address(0), "DeployOPChainInput: not set"); return _batcher; @@ -355,7 +362,8 @@ contract DeployOPChain is Script { batcher: _doi.batcher(), unsafeBlockSigner: _doi.unsafeBlockSigner(), proposer: _doi.proposer(), - challenger: _doi.challenger() + challenger: _doi.challenger(), + systemConfigFeeAdmin: _doi.systemConfigFeeAdmin() }); OPContractsManager.DeployInput memory deployInput = OPContractsManager.DeployInput({ roles: roles, @@ -572,7 +580,7 @@ contract DeployOPChain is Script { function assertValidL1CrossDomainMessenger(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1CrossDomainMessenger messenger = _doo.l1CrossDomainMessengerProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); + DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-10"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-20"); @@ -581,15 +589,16 @@ contract DeployOPChain is Script { require(address(messenger.portal()) == address(_doo.optimismPortalProxy()), "L1xDM-40"); require(address(messenger.superchainConfig()) == address(_doi.opcmProxy().superchainConfig()), "L1xDM-50"); - bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204))); - require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60"); + // TODO: vm.expectRevert is not supported by op-chain-ops, so we can't check this yet. + // vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); + // messenger.xDomainMessageSender(); } function assertValidL1StandardBridge(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1StandardBridge bridge = _doo.l1StandardBridgeProxy(); IL1CrossDomainMessenger messenger = _doo.l1CrossDomainMessengerProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); require(address(bridge.MESSENGER()) == address(messenger), "L1SB-10"); require(address(bridge.messenger()) == address(messenger), "L1SB-20"); @@ -601,7 +610,7 @@ contract DeployOPChain is Script { function assertValidOptimismMintableERC20Factory(DeployOPChainInput, DeployOPChainOutput _doo) internal { IOptimismMintableERC20Factory factory = _doo.optimismMintableERC20FactoryProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); require(factory.BRIDGE() == address(_doo.l1StandardBridgeProxy()), "MERC20F-10"); require(factory.bridge() == address(_doo.l1StandardBridgeProxy()), "MERC20F-20"); @@ -610,7 +619,7 @@ contract DeployOPChain is Script { function assertValidL1ERC721Bridge(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1ERC721Bridge bridge = _doo.l1ERC721BridgeProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20"); diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index 74492556e1b1..f2f39e660acd 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -359,6 +359,7 @@ contract DeploySuperchain is Script { function deployAndInitializeSuperchainConfig(DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public { address guardian = _dsi.guardian(); + address upgrader = _dsi.superchainProxyAdminOwner(); bool paused = _dsi.paused(); IProxyAdmin superchainProxyAdmin = _dso.superchainProxyAdmin(); @@ -376,7 +377,7 @@ contract DeploySuperchain is Script { superchainProxyAdmin.upgradeAndCall( payable(address(superchainConfigProxy)), address(superchainConfigImpl), - abi.encodeCall(ISuperchainConfig.initialize, (guardian, paused)) + abi.encodeCall(ISuperchainConfig.initialize, (guardian, upgrader, paused)) ); vm.stopBroadcast(); diff --git a/packages/contracts-bedrock/scripts/libraries/Config.sol b/packages/contracts-bedrock/scripts/libraries/Config.sol index 18084761e807..4aca8bc31e3c 100644 --- a/packages/contracts-bedrock/scripts/libraries/Config.sol +++ b/packages/contracts-bedrock/scripts/libraries/Config.sol @@ -34,10 +34,11 @@ enum Fork { ECOTONE, FJORD, GRANITE, - HOLOCENE + HOLOCENE, + ISTHMUS } -Fork constant LATEST_FORK = Fork.HOLOCENE; +Fork constant LATEST_FORK = Fork.ISTHMUS; library ForkUtils { function toString(Fork _fork) internal pure returns (string memory) { @@ -53,6 +54,8 @@ library ForkUtils { return "granite"; } else if (_fork == Fork.HOLOCENE) { return "holocene"; + } else if (_fork == Fork.ISTHMUS) { + return "isthmus"; } else { return "unknown"; } @@ -168,6 +171,8 @@ library Config { return Fork.GRANITE; } else if (forkHash == keccak256(bytes("holocene"))) { return Fork.HOLOCENE; + } else if (forkHash == keccak256(bytes("isthmus"))) { + return Fork.ISTHMUS; } else { revert(string.concat("Config: unknown fork: ", forkStr)); } diff --git a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol index da9aea12a0ac..4197746ca436 100644 --- a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol +++ b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol @@ -351,6 +351,11 @@ library DeployUtils { } } + /// @notice The unstructured storage slot that is used for v5 openzeppelin initializable. Computed as: + /// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & + /// ~bytes32(uint256(0xff)) + bytes32 internal constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; + /// @notice Asserts that for a given contract the value of a storage slot at an offset is 1 or /// `type(uint8).max`. The value is set to 1 when a contract is initialized, and set to /// `type(uint8).max` when `_disableInitializers` is called. @@ -362,4 +367,9 @@ library DeployUtils { "DeployUtils: value at the given slot and offset does not indicate initialization" ); } + + /// @notice Asserts that the contract has been initialized, assuming that it is using openzeppelin v5 initializable. + function assertInitialized(address _contractAddress) internal view { + assertInitialized({ _contractAddress: _contractAddress, _slot: uint256(INITIALIZABLE_STORAGE), _offset: 0 }); + } } diff --git a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol index 944206694d78..a646a7b6d02f 100644 --- a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol +++ b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol @@ -5,6 +5,7 @@ import { Vm } from "forge-std/Vm.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { LibString } from "@solady/utils/LibString.sol"; import { Executables } from "scripts/libraries/Executables.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Process } from "scripts/libraries/Process.sol"; /// @notice Contains information about a storage slot. Mirrors the layout of the storage @@ -190,6 +191,19 @@ library ForgeArtifacts { function getInitializedSlot(string memory _contractName) internal returns (StorageSlot memory slot_) { string memory storageLayout = getStorageLayout(_contractName); + // Contracts that use oz v5 should be here + if (LibString.eq(_contractName, "L1ERC721Bridge")) { + StorageSlot memory slot = StorageSlot({ + astId: 0, + _contract: _contractName, + label: "_initialized", + offset: 0, + slot: vm.toString(DeployUtils.INITIALIZABLE_STORAGE), + _type: "bool" + }); + return slot; + } + // FaultDisputeGame and PermissionedDisputeGame use a different name for the initialized storage slot. string memory slotName = "_initialized"; string memory slotType = "t_uint8"; @@ -214,7 +228,10 @@ library ForgeArtifacts { slotType, "\")'" ); - bytes memory rawSlot = vm.parseJson(string(Process.run(command))); + + bytes memory result = Process.run(command); + require(result.length > 0, string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); + bytes memory rawSlot = vm.parseJson(string(result)); slot_ = abi.decode(rawSlot, (StorageSlot)); } diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 1e14f5d286b5..0af4722ac9da 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -8,56 +8,60 @@ "sourceCodeHash": "0x30e83a535ef27b2e900c831c4e1a4ec2750195350011c4fdacda1da9db2d167b" }, "src/L1/L1CrossDomainMessenger.sol": { - "initCodeHash": "0x2e9cb3ceb5e55341b311f0666ef7655df4fafae75afdfbcd701cd9c9b2b017d5", - "sourceCodeHash": "0x848ec3774be17bcc8ba65a23d08e35e979b3f39f9d2ac8a810188f945c69c9ea" + "initCodeHash": "0x8d0351b1c13be9e53ba9d3c6174e0ef0ae5c37cfdf31da446533548f5c56553a", + "sourceCodeHash": "0x5b00fc7b2450c008cefb283779237f7402722182eb9e5210439d5a01359dc38f" }, "src/L1/L1ERC721Bridge.sol": { - "initCodeHash": "0xb3bf093ea83a24574a6093bebf5b2aea707355ed8d6702b2b5eb292e75b6ae42", - "sourceCodeHash": "0x289de9f40898b6305deecc6b60cdf566aa6c6a1444f713c3a0af23ea7878207e" + "initCodeHash": "0x19542aad97e146219d4d3cdcfb29f8f1591815731c51216e8424e754556a21a0", + "sourceCodeHash": "0x71fc58927450aa7f6e97688e7a5ecbfadfa235a26523d4c540183647ccbf7fdf" + }, + "src/L1/L1OptimismMintableERC20Factory.sol": { + "initCodeHash": "0xc6b0a84d485c8503bd1ce077fbfa5533ad1f6a001feab31d9c21948d02d47827", + "sourceCodeHash": "0x9432cd22e23347f4b397706b4eb28ee4eaba481f4914d2d685d77c413963326d" }, "src/L1/L1StandardBridge.sol": { - "initCodeHash": "0x802f72745bb9a82dc049377bb9cf6b58f35aec388aeb957b28a5e14f28d91bc1", - "sourceCodeHash": "0x24b784645b065a5393a2115a078d67f91eb09afd5e70baf81daf975381f16155" + "initCodeHash": "0x0a17e22cb56c915bf1aaf3a61ac28de2dca4f2122ba57fe597a3dd612e245478", + "sourceCodeHash": "0xa706c8d55d1e34a86d42a113cd486ccb809a36ff956cf32fbcb839ca2cdfbf0b" }, "src/L1/L2OutputOracle.sol": { "initCodeHash": "0x1182bfb87c4ab399b912ca7fe18cdbf4b24c414e078fb0a55bd3c44d442d3ed1", "sourceCodeHash": "0x4132ff37d267cb12224b75ea806c0aa7d25407b0d66ce526d7fcda8f7d223882" }, "src/L1/OPContractsManager.sol": { - "initCodeHash": "0xd58cb3978affc5c1457cdd498ff8420c90aef804d4c3b62cf42ab2691986d6d2", - "sourceCodeHash": "0x7bfa6eff76176649fe600303cd60009a0f6e282cbaec55836b5ea1f8875cbeb5" + "initCodeHash": "0xa6e3fda99d87e69c3a2d2b1efb2f419cae75b1f2a0295548f5f2db84432dd9c4", + "sourceCodeHash": "0x647ec3500bca93169dd5384919c6787238da40ffd53fc5d1c3b7c61701a9d398" }, "src/L1/OptimismPortal.sol": { - "initCodeHash": "0x152167cfa18635ae4918a6eb3371a599cfa084418c0a652799cdb48bfc0ee0cc", - "sourceCodeHash": "0xbe34b82900d02f71bb0949818eabe49531f7e0d8d8bae01f6dac4a296530d1aa" + "initCodeHash": "0x25c4212bac52f172b193cb3c36376074f239491d53c0d83c5081df7042cd02f8", + "sourceCodeHash": "0x6300532cb22fd9848bf54891c3a48003c7108470199a200d082b740c91e0a017" }, "src/L1/OptimismPortal2.sol": { - "initCodeHash": "0x218358b48f640b3fcb2d239f00dc1cd3b11517ad46c8e1efa44953d38da63540", - "sourceCodeHash": "0x66ac1212760db53a2bb1839e4cd17dc071d9273b8e6fb80646b79e91b3371c1a" + "initCodeHash": "0x8e6c808992ea90f28feb12338309ff512876163ed4409a1dca6184475ab4a136", + "sourceCodeHash": "0xedc1819877c3263c43a27c5077363691ce0839fa8e61b8d000a0ba9998bf1905" }, "src/L1/OptimismPortalInterop.sol": { - "initCodeHash": "0x39f66ac74341ec235fbdd0d79546283210bd8ac35a2ab2c4bd36c9722ce18411", - "sourceCodeHash": "0xbb98144285b9530e336f957d10b20363b350876597e30fd34821940896a2bae8" + "initCodeHash": "0xcda63947d6fb33478b7bdc6f20a983061b3ff9d77a7c64a6b7141af08b79fd60", + "sourceCodeHash": "0x3731c4ac1c5784b92934d3f84d8eba70321b4c6b379a9349aa4c620da6c5a7d8" }, "src/L1/ProtocolVersions.sol": { "initCodeHash": "0xefd4806e8737716d5d2022ca2e9e9fba0a0cb5714b026166b58e472222c7d15f", "sourceCodeHash": "0x15205131bf420aa6d03c558bb75dd49cd7439caed7ccdcbfd89c4170a48c94f5" }, "src/L1/SuperchainConfig.sol": { - "initCodeHash": "0xfca12d9016c746e5c275b186e0ca40cfd65cf45a5665aab7589a669fea3abb47", - "sourceCodeHash": "0x39489a85bc3a5c8560f82d41b31bf7fe22f5b648f4ed538f61695a73092ea9eb" + "initCodeHash": "0x37b2cabf25e5717ec9961bdb084b3a35ffd403c867361b106ea5e47345bc7ba9", + "sourceCodeHash": "0xbd02a2297b9088c29070bf910ec306dd31d8d185007afaaf7743435493b0ede6" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0x429058f75d97fa7a7d0166b59830909bc722324feefc40f2b41419d6335d3f37", - "sourceCodeHash": "0x5ca776041a4ddc0d28ec55db7012d669481cd4601b0e71dbd3493a67b8a7e5a5" + "initCodeHash": "0xc2961b2de5e12fc7f8079bb462ee9ca605940ab6072b531b5fa173dd3d3af407", + "sourceCodeHash": "0xa699d0715f3016f3acb3e3971d8560c280340a0c87e8a0a07ab80160e612c196" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x277a61dcabed81a15739a8e9ed50615252bcc687cebea852e00191d0a1fbe11f", - "sourceCodeHash": "0x38361a4f70a19e1b7819e933932a0c9fd2bcebaaebcbc7942f5c00dfaa2c28df" + "initCodeHash": "0x2f5b58ef3db81ee004e69069d231de7c108920105edafad5e0fcf8a897201d5d", + "sourceCodeHash": "0x5c996568f655a7467e9de456b39eed7dbfde46d53a609f56b7369940fef0e1ea" }, "src/L2/BaseFeeVault.sol": { - "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", - "sourceCodeHash": "0x983e8e248c61e362ba6a01dd2e217a535c9bb828dc0b4421f5f27e0577f2e14c" + "initCodeHash": "0x2eded104b7c98ea055dbd02a19ed967f5341ba169056425522ef2b3ce4b3300e", + "sourceCodeHash": "0x8428d0a173d3d58327aebc5585f1a62e3929ed56324a224e15b88bd1bca31348" }, "src/L2/CrossL2Inbox.sol": { "initCodeHash": "0x31ecaebf368ab3333e80c6dc004b3c9f9a31f813c3138ab388bb3eead9f1b4ee", @@ -68,60 +72,68 @@ "sourceCodeHash": "0x0b6afdc52d1ae88d9e4bbb5dc00920e7a6bd1e9d6595bfdbae64874190f39df0" }, "src/L2/GasPriceOracle.sol": { - "initCodeHash": "0x7e8c2b42e10187ad649c0bf70c5688c2a4af3c412bacaec87d63c3f93ae4cfef", - "sourceCodeHash": "0xa12ce15ded3cca681b2fc9facaebbb45d740dd6f9c9496333c1c46689c9a2d99" + "initCodeHash": "0x83d50e3b34cd1b4de32f1cced28796b07aefc526cc17ceb1903ad55f4abc90b7", + "sourceCodeHash": "0x64109614b3813b0f6003bcfada1bdbdba733c623aa7b935b9e51753f1c60ed6a" }, "src/L2/L1Block.sol": { - "initCodeHash": "0xa919d2aa76a7ecdfd076e2b1dbece499cc85706075f16eb6fa7b1a0fa7b38c1b", - "sourceCodeHash": "0x692cfcbc06dba6328f6e5c6b500741df04e4bdf730b2069aeb5d168355ea7b6f" + "initCodeHash": "0xdd942ad49044ca92b46f39f5035995628c2c8b10c1060c0a59b65cdc20176832", + "sourceCodeHash": "0x4c6be03e3eddc17810a65e475c1c7dc1e5a15fe4c1c6d9b8ab17b3323d06e405" }, "src/L2/L1BlockInterop.sol": { - "initCodeHash": "0x62e9cc59daaf72066ac20597a666db33e9a7b3f7be71a3d47ea4841a9aca9d07", - "sourceCodeHash": "0xe57627347366d74029a0d24f0b45d7b9cf82b81c94681d0f633d5e5c37c8de4a" + "initCodeHash": "0xfab767c50aff6bd9d232605c7016afaee34e36647142ea952fea57c98b860c2e", + "sourceCodeHash": "0xc220f197eb6aec98554fc72f68f95578a20f201a4780cada28832d21ae4d816b" }, "src/L2/L1FeeVault.sol": { - "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", - "sourceCodeHash": "0xc7cda130f2bb3648e04d5a480082aa1789e16456c1280954d822b05d30100b2d" + "initCodeHash": "0xffd78f3c10c018ec77c3ede7599f76ad23433cc1f18794e47eb73ed5f3460dda", + "sourceCodeHash": "0x52cec09ff02282b9e7adef9f770cbc1f31682d41aece1749ba7b4ff14528eb1b" }, "src/L2/L2CrossDomainMessenger.sol": { - "initCodeHash": "0xc496495496b96ea0eaf417c5e56b295836c12db3e6aafe2e607563e7a50b5b65", - "sourceCodeHash": "0x56edf0f36366326a92722ae3c7502bce3d80b2ee5e354181dc09ba801437a488" + "initCodeHash": "0x9d478585dbebd5bf03e4d7a735bc5f64006e2ff99d62fc08c2202c5483005d5a", + "sourceCodeHash": "0x0726b306b41eaf326e19f0d6d1c8a9dbdc8efb2baa449e8fca5954a7b31f79c7" }, "src/L2/L2ERC721Bridge.sol": { - "initCodeHash": "0xaed0528e8b81817a0c3b41513c02e7fd678f58e34b98f02ea33d5a770a064c2f", - "sourceCodeHash": "0xf8569c75b801f38f8a5a41e94e90f159ddc5f5412804b26e3e564755a50631b8" + "initCodeHash": "0xc83703c9b18f7e4dcdbaf327894e36bf1edfbd39f9e55c5546f2f2deedf7689f", + "sourceCodeHash": "0x69e4394ac1cabc4c2cbf64988fc234f47c0750af720875ed2dbd339b752f6b87" + }, + "src/L2/L2OptimismMintableERC20Factory.sol": { + "initCodeHash": "0xb4411155cd6bf9ce710cbce80aaf7a5c8d4b509cfb3b6eca1ff1bcb9eee76013", + "sourceCodeHash": "0xb2330d5ef6c7eecc14745d5a2122daa105f2630f811548c203e5bca4a91787ec" }, "src/L2/L2StandardBridge.sol": { - "initCodeHash": "0xcb4aa19f0cd43a35cb5c65f26c3cfd7c41f1d1e5bcc15aef6096d385df7272c9", - "sourceCodeHash": "0x89771b53b7f6e64d943afb2a4bf15395efcf20d5302b76a18e52fa7cce8cdc56" + "initCodeHash": "0xf6a2658d9fbda6959cff4af0ae3891534fdfdc2685c6f7210bb124ba6553d5a3", + "sourceCodeHash": "0x909e93976c2d9b419a52ce131d86aad5de9c0e8fe6bf1d5afc7257c1d9524871" }, "src/L2/L2StandardBridgeInterop.sol": { - "initCodeHash": "0xc4eaece28d2cfca3c51247c3cce320a167a83c7fd13aea5736549d2b25e0b139", - "sourceCodeHash": "0x9e80044adf5f83c30b520ee153b75be5a152081c9e1271e7e618ecfccd1fb4ac" + "initCodeHash": "0x26c04ae665855e91fc12e995bc9436b63415068da90d694f4a9f070db80027b2", + "sourceCodeHash": "0x0ca6d8900a02c213acca44c8cb56df816fa1bebd6903a9ea6d4c21525c936c12" }, "src/L2/L2ToL1MessagePasser.sol": { "initCodeHash": "0x13fe3729beb9ed966c97bef09acb9fe5043fe651d453145073d05f2567fa988d", "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x2a1a1ee4f47175ce661ee8e4e50cfa879b082dcb5278b1d66ddda00ed77bb744", - "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" + "initCodeHash": "0x8db6e86984e2501a4281765a9eaeafc0fe1d15c7bb802bbc15d5221a907be526", + "sourceCodeHash": "0x00e6d19b377aad27dfc8bf759dbc052fcc708d17e2a56453988ccc97e838399d" + }, + "src/L2/OptimismMintableERC721Factory.sol": { + "initCodeHash": "0xc66a3211c520beb0e3da3ce78c0f307bf4397365f147e45d31650d15678e42b2", + "sourceCodeHash": "0x35b725198c3780182408ccc76dc877b29659508592befde437754c69af7d7b5f" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9", - "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c" + "initCodeHash": "0x529f3756a365a8f225d7debdd15c474c1338c8973bb65a1aad98f85753f74722", + "sourceCodeHash": "0xf68baaee0a09ea51d5a4e821df79976c0914369ebc8e5fd27bbbf89072254fc8" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { - "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", - "sourceCodeHash": "0xf4379e49665823c877f5732f35068435ce06e2394fce6910a5e113d16cdc9f95" + "initCodeHash": "0xb032e99f5c205c8b474da89887e350277cdd05b99ee28374b97bfae18ef7a72c", + "sourceCodeHash": "0xe8753177e8491152eb3ecbed42d5232ae545091a116f23d104a4f9621711fbda" }, "src/L2/OptimismSuperchainERC20Factory.sol": { - "initCodeHash": "0x18a362c57f08b611db98dfde96121385e938f995c84e3547c1c03fd49f9db2fd", - "sourceCodeHash": "0x450cd89d0aae7bbc85ff57a14a6d3468c24c6743f25943f6d895d34b1456c456" + "initCodeHash": "0xe6a098346699c3df248050fc3301197661d391f8e4849a93f00afeac17cae317", + "sourceCodeHash": "0x1e8380bdce27292f805ddc85fec0db5bc60fbe3fd9e0bcbd146c103062ed459a" }, "src/L2/SequencerFeeVault.sol": { - "initCodeHash": "0xcaadbf08057b5d47f7704257e9385a29e42a7a08c818646d109c5952d3d35218", - "sourceCodeHash": "0x05bbc6039e5a9ff38987e7b9b89c69e2ee8aa4b7ca20dd002ea1bbd3d70f27f3" + "initCodeHash": "0x4f0017a0b906cb949ebb5931cebe00130109a4e9bb453d77b470982de5b996fb", + "sourceCodeHash": "0x8d71ff1b7d5c758ff15137c0ab1411e4abe8d91bc4fab28c981a767f6323b426" }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", @@ -132,12 +144,12 @@ "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97", - "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359" + "initCodeHash": "0xb74ce648b49e3b7885a3252ff32aeb71e8cb515558f516c0dd61f1cf22028edc", + "sourceCodeHash": "0x095de6eeb2cd06937b3fe014392048bc494cb5ca7a4a5fd5519141a80eae3a76" }, "src/L2/WETH.sol": { - "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", - "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" + "initCodeHash": "0x480d4f8dbec1b0d3211bccbbdfb69796f3e90c784f724b1bbfd4703b0aafdeba", + "sourceCodeHash": "0x59a210accdd484cf00e63042cde4687e1c87ef9dbafa5fe2e081dee8e6563621" }, "src/cannon/MIPS.sol": { "initCodeHash": "0xa3cbf121bad13c00227ea4fef128853d9a86b7ec9158de894f99b58d38d7630a", @@ -215,17 +227,9 @@ "initCodeHash": "0x9cd677275b175812f1d5f90a127dbf7b3592714fd842a7a0de3988d716ca3eac", "sourceCodeHash": "0x5611d8082f68af566554d7f09640b4b1f0e3efee4da1372b68fc7fc538a35ac7" }, - "src/universal/OptimismMintableERC20Factory.sol": { - "initCodeHash": "0x03ad07bd7f89a29f1850fa8b5d377daf0e1d5aef6cb458a127df520549e8e8e6", - "sourceCodeHash": "0xdb6ec93782a4a217475195507740794a4f5553b9032e7ba31dc48b81f579a940" - }, "src/universal/OptimismMintableERC721.sol": { - "initCodeHash": "0x8aa309f2676d5267b6c9e411f88dc6e4badce414b8d66b330df3f60e9836380e", - "sourceCodeHash": "0x03bf7ad4d2b751bdead9930fc8f89b8e55d40dd4b2f5670fd339e87ae81f8b49" - }, - "src/universal/OptimismMintableERC721Factory.sol": { - "initCodeHash": "0x5ea977ba35558c3b75bebe28900548c763d205e40d6cf7660292b8e96bf3aea8", - "sourceCodeHash": "0x063ca3a0a2e3c592173af6157e383b5aaeff752000f98648a5c71260bb26590a" + "initCodeHash": "0x406ff004414b60852afe6ee6783ac03157ebcbe7e4787740a94576594fdfe912", + "sourceCodeHash": "0x8b4292d7556907b597d9fd3ccd5bcd8b8d61bf8e32c5a7a33d4de2557414c830" }, "src/universal/StorageSetter.sol": { "initCodeHash": "0x21b3059e9b13b330f76d02b61f61dcfa3abf3517a0b56afa0895c4b8291740bf", diff --git a/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json b/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json index b745bcb8184c..80ccfb99162a 100644 --- a/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json @@ -1,25 +1,4 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minWithdrawalAmount", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "_withdrawalNetwork", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "stateMutability": "payable", "type": "receive" @@ -56,7 +35,30 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "", + "name": "withdrawalNetwork_", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "config", + "outputs": [ + { + "internalType": "address", + "name": "recipient_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "withdrawalNetwork_", "type": "uint8" } ], @@ -128,7 +130,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "network_", + "name": "withdrawalNetwork_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json deleted file mode 100644 index 0637a088a01e..000000000000 --- a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json deleted file mode 100644 index 0637a088a01e..000000000000 --- a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1Block.json b/packages/contracts-bedrock/snapshots/abi/L1Block.json index 020c9e942c75..d477c46ddad5 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1Block.json +++ b/packages/contracts-bedrock/snapshots/abi/L1Block.json @@ -121,6 +121,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum Types.ConfigType", + "name": "_type", + "type": "uint8" + } + ], + "name": "getConfig", + "outputs": [ + { + "internalType": "bytes", + "name": "data_", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "hash", @@ -147,6 +166,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "isIsthmus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "l1FeeOverhead", @@ -199,6 +231,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum Types.ConfigType", + "name": "_type", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "_value", + "type": "bytes" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -227,6 +277,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "setIsthmus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -343,5 +400,10 @@ "inputs": [], "name": "NotDepositor", "type": "error" + }, + { + "inputs": [], + "name": "UnsafeCast", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json index ab089f0cec55..35ce78e5f5fd 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json @@ -141,6 +141,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum Types.ConfigType", + "name": "_type", + "type": "uint8" + } + ], + "name": "getConfig", + "outputs": [ + { + "internalType": "bytes", + "name": "data_", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "hash", @@ -199,6 +218,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "isIsthmus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "l1FeeOverhead", @@ -254,7 +286,7 @@ { "inputs": [ { - "internalType": "enum ConfigType", + "internalType": "enum Types.ConfigType", "name": "_type", "type": "uint8" }, @@ -297,6 +329,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "setIsthmus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -471,5 +510,10 @@ "inputs": [], "name": "NotDepositor", "type": "error" + }, + { + "inputs": [], + "name": "UnsafeCast", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json index 22a4353cc656..fb4798a3d662 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json @@ -223,7 +223,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json b/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json index 33cd5b0a850b..0c6ccbd23684 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json @@ -209,7 +209,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -342,12 +342,22 @@ "inputs": [ { "indexed": false, - "internalType": "uint8", + "internalType": "uint64", "name": "version", - "type": "uint8" + "type": "uint64" } ], "name": "Initialized", "type": "event" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json b/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json index b745bcb8184c..80ccfb99162a 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json @@ -1,25 +1,4 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minWithdrawalAmount", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "_withdrawalNetwork", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "stateMutability": "payable", "type": "receive" @@ -56,7 +35,30 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "", + "name": "withdrawalNetwork_", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "config", + "outputs": [ + { + "internalType": "address", + "name": "recipient_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "withdrawalNetwork_", "type": "uint8" } ], @@ -128,7 +130,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "network_", + "name": "withdrawalNetwork_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/abi/L1OptimismMintableERC20Factory.json similarity index 100% rename from packages/contracts-bedrock/snapshots/abi/OptimismMintableERC20Factory.json rename to packages/contracts-bedrock/snapshots/abi/L1OptimismMintableERC20Factory.json diff --git a/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json b/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json index 45480499fe9f..d79fbe616a57 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json @@ -26,7 +26,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } @@ -447,7 +447,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -468,12 +468,12 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json index 717bbb6eb6f4..c9959ad9e49f 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json @@ -1,9 +1,4 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "inputs": [], "name": "MESSAGE_VERSION", @@ -164,19 +159,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract CrossDomainMessenger", - "name": "_l1CrossDomainMessenger", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "l1CrossDomainMessenger", @@ -348,19 +330,6 @@ "name": "FailedRelayedMessage", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json b/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json index e9578ad9726c..8b3af6fcc3eb 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json @@ -1,9 +1,4 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "inputs": [], "name": "MESSENGER", @@ -139,19 +134,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_l1ERC721Bridge", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "messenger", @@ -162,7 +144,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -289,18 +271,5 @@ ], "name": "ERC721BridgeInitiated", "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json new file mode 100644 index 000000000000..0a5fddb96906 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json @@ -0,0 +1,196 @@ +[ + { + "inputs": [], + "name": "BRIDGE", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_remoteToken", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "name": "createOptimismMintableERC20", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_remoteToken", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + } + ], + "name": "createOptimismMintableERC20WithDecimals", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_remoteToken", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "name": "createStandardL2Token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "deployments", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "localToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "remoteToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "deployer", + "type": "address" + } + ], + "name": "OptimismMintableERC20Created", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "remoteToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "localToken", + "type": "address" + } + ], + "name": "StandardL2TokenCreated", + "type": "event" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json new file mode 100644 index 000000000000..8dbba1e08992 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json @@ -0,0 +1,300 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_newAdmin", + "type": "address" + } + ], + "name": "changeProxyAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + } + ], + "name": "getProxyAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_proxy", + "type": "address" + } + ], + "name": "getProxyImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "implementationName", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isUpgrading", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "proxyType", + "outputs": [ + { + "internalType": "enum ProxyAdmin.ProxyType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IAddressManager", + "name": "_address", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + } + ], + "name": "setImplementationName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "enum ProxyAdmin.ProxyType", + "name": "_type", + "type": "uint8" + } + ], + "name": "setProxyType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_upgrading", + "type": "bool" + } + ], + "name": "setUpgrading", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "upgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "upgradeAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json b/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json index e562034818d5..44ecfa34094c 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json @@ -1,9 +1,4 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "stateMutability": "payable", "type": "receive" @@ -26,7 +21,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } @@ -236,19 +231,6 @@ "stateMutability": "payable", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract StandardBridge", - "name": "_otherBridge", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "l1TokenBridge", @@ -272,7 +254,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -280,7 +262,7 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } @@ -566,19 +548,6 @@ "name": "ETHBridgeInitiated", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json b/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json index b2dc8dddd050..1d5f7aacfc7b 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json @@ -21,7 +21,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } @@ -254,19 +254,6 @@ "stateMutability": "payable", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract StandardBridge", - "name": "_otherBridge", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "l1TokenBridge", @@ -290,7 +277,7 @@ "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -298,7 +285,7 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract StandardBridge", + "internalType": "contract IStandardBridge", "name": "", "type": "address" } @@ -615,19 +602,6 @@ "name": "ETHBridgeInitiated", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 7c478feb235d..7c858ca892ee 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -137,6 +137,11 @@ "internalType": "address", "name": "challenger", "type": "address" + }, + { + "internalType": "address", + "name": "systemConfigFeeAdmin", + "type": "address" } ], "internalType": "struct OPContractsManager.Roles", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json index 7c478feb235d..7c858ca892ee 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json @@ -137,6 +137,11 @@ "internalType": "address", "name": "challenger", "type": "address" + }, + { + "internalType": "address", + "name": "systemConfigFeeAdmin", + "type": "address" } ], "internalType": "struct OPContractsManager.Roles", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json index 35250255a9ba..58e3271abc92 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json @@ -2,7 +2,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IL2ERC721Bridge", "name": "_bridge", "type": "address" }, @@ -35,7 +35,7 @@ "name": "BRIDGE", "outputs": [ { - "internalType": "address", + "internalType": "contract IL2ERC721Bridge", "name": "", "type": "address" } @@ -124,7 +124,7 @@ "name": "bridge", "outputs": [ { - "internalType": "address", + "internalType": "contract IL2ERC721Bridge", "name": "", "type": "address" } diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json index e6ecac1d9381..0137a7bb20ae 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json @@ -1,31 +1,15 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_bridge", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_remoteChainId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "inputs": [], "name": "BRIDGE", "outputs": [ { - "internalType": "address", + "internalType": "contract IL2ERC721Bridge", "name": "", "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -46,12 +30,12 @@ "name": "bridge", "outputs": [ { - "internalType": "address", + "internalType": "contract IL2ERC721Bridge", "name": "", "type": "address" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -104,7 +88,7 @@ }, { "inputs": [], - "name": "remoteChainID", + "name": "remoteChainId", "outputs": [ { "internalType": "uint256", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json index 7ccee328a984..ed8ec82591ed 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json @@ -413,27 +413,17 @@ { "inputs": [ { - "internalType": "address", - "name": "_token", - "type": "address" - }, - { - "internalType": "uint8", - "name": "_decimals", + "internalType": "enum Types.ConfigType", + "name": "_type", "type": "uint8" }, { - "internalType": "bytes32", - "name": "_name", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_symbol", - "type": "bytes32" + "internalType": "bytes", + "name": "_value", + "type": "bytes" } ], - "name": "setGasPayingToken", + "name": "setConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 2f52ed573d37..b4bd5efa8df3 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -605,27 +605,17 @@ { "inputs": [ { - "internalType": "address", - "name": "_token", - "type": "address" - }, - { - "internalType": "uint8", - "name": "_decimals", + "internalType": "enum Types.ConfigType", + "name": "_type", "type": "uint8" }, { - "internalType": "bytes32", - "name": "_name", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_symbol", - "type": "bytes32" + "internalType": "bytes", + "name": "_value", + "type": "bytes" } ], - "name": "setGasPayingToken", + "name": "setConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -669,6 +659,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "upgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json index 5b9f72b9446c..b4bd5efa8df3 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json @@ -605,7 +605,7 @@ { "inputs": [ { - "internalType": "enum ConfigType", + "internalType": "enum Types.ConfigType", "name": "_type", "type": "uint8" }, @@ -620,34 +620,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "_token", - "type": "address" - }, - { - "internalType": "uint8", - "name": "_decimals", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_name", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_symbol", - "type": "bytes32" - } - ], - "name": "setGasPayingToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -687,6 +659,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "upgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json b/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json index 700e7d7b9810..6412128462e0 100644 --- a/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json @@ -1,25 +1,4 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minWithdrawalAmount", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "_withdrawalNetwork", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "stateMutability": "payable", "type": "receive" @@ -56,7 +35,30 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "", + "name": "withdrawalNetwork_", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "config", + "outputs": [ + { + "internalType": "address", + "name": "recipient_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "withdrawalNetwork_", "type": "uint8" } ], @@ -69,7 +71,7 @@ "outputs": [ { "internalType": "address", - "name": "", + "name": "recipient_", "type": "address" } ], @@ -141,7 +143,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "network_", + "name": "withdrawalNetwork_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json index 0304f507eb50..e61f005d9aa2 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json @@ -30,6 +30,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "UPGRADER_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "guardian", @@ -50,6 +63,11 @@ "name": "_guardian", "type": "address" }, + { + "internalType": "address", + "name": "_upgrader", + "type": "address" + }, { "internalType": "bool", "name": "_paused", @@ -94,6 +112,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "upgrader", + "outputs": [ + { + "internalType": "address", + "name": "upgrader_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json index b7e18556fa2c..7f086dfad600 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json @@ -225,6 +225,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "feeAdmin", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "gasLimit", @@ -285,9 +298,31 @@ { "inputs": [ { - "internalType": "address", - "name": "_owner", - "type": "address" + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "feeAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "unsafeBlockSigner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "batcherHash", + "type": "bytes32" + } + ], + "internalType": "struct SystemConfig.Roles", + "name": "_roles", + "type": "tuple" }, { "internalType": "uint32", @@ -299,21 +334,11 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, - { - "internalType": "bytes32", - "name": "_batcherHash", - "type": "bytes32" - }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, - { - "internalType": "address", - "name": "_unsafeBlockSigner", - "type": "address" - }, { "components": [ { @@ -630,6 +655,34 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum Types.ConfigType", + "name": "_type", + "type": "uint8" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_min", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "_network", + "type": "uint8" + } + ], + "name": "setFeeVaultConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -800,5 +853,10 @@ ], "name": "OwnershipTransferred", "type": "event" + }, + { + "inputs": [], + "name": "UnsafeCast", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json index a459af15801b..a0fc03aa0b3b 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json @@ -246,6 +246,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "feeAdmin", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "gasLimit", @@ -306,9 +319,31 @@ { "inputs": [ { - "internalType": "address", - "name": "_owner", - "type": "address" + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "feeAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "unsafeBlockSigner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "batcherHash", + "type": "bytes32" + } + ], + "internalType": "struct SystemConfig.Roles", + "name": "_roles", + "type": "tuple" }, { "internalType": "uint32", @@ -320,21 +355,11 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, - { - "internalType": "bytes32", - "name": "_batcherHash", - "type": "bytes32" - }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, - { - "internalType": "address", - "name": "_unsafeBlockSigner", - "type": "address" - }, { "components": [ { @@ -418,11 +443,6 @@ "internalType": "struct SystemConfig.Addresses", "name": "_addresses", "type": "tuple" - }, - { - "internalType": "address", - "name": "_dependencyManager", - "type": "address" } ], "name": "initialize", @@ -433,9 +453,31 @@ { "inputs": [ { - "internalType": "address", - "name": "_owner", - "type": "address" + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "feeAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "unsafeBlockSigner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "batcherHash", + "type": "bytes32" + } + ], + "internalType": "struct SystemConfig.Roles", + "name": "_roles", + "type": "tuple" }, { "internalType": "uint32", @@ -447,21 +489,11 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, - { - "internalType": "bytes32", - "name": "_batcherHash", - "type": "bytes32" - }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, - { - "internalType": "address", - "name": "_unsafeBlockSigner", - "type": "address" - }, { "components": [ { @@ -545,6 +577,11 @@ "internalType": "struct SystemConfig.Addresses", "name": "_addresses", "type": "tuple" + }, + { + "internalType": "address", + "name": "_dependencyManager", + "type": "address" } ], "name": "initialize", @@ -791,6 +828,34 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum Types.ConfigType", + "name": "_type", + "type": "uint8" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_min", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "_network", + "type": "uint8" + } + ], + "name": "setFeeVaultConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -961,5 +1026,10 @@ ], "name": "OwnershipTransferred", "type": "event" + }, + { + "inputs": [], + "name": "UnsafeCast", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json deleted file mode 100644 index 9bc33d142741..000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "bytes": "20", - "label": "spacer_0_0_20", - "offset": 0, - "slot": "0", - "type": "address" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json deleted file mode 100644 index ff157f517694..000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json +++ /dev/null @@ -1,65 +0,0 @@ -[ - { - "bytes": "1600", - "label": "spacer_1_0_1600", - "offset": 0, - "slot": "0", - "type": "uint256[50]" - }, - { - "bytes": "20", - "label": "spacer_51_0_20", - "offset": 0, - "slot": "50", - "type": "address" - }, - { - "bytes": "1568", - "label": "spacer_52_0_1568", - "offset": 0, - "slot": "51", - "type": "uint256[49]" - }, - { - "bytes": "1", - "label": "spacer_101_0_1", - "offset": 0, - "slot": "100", - "type": "bool" - }, - { - "bytes": "1568", - "label": "spacer_102_0_1568", - "offset": 0, - "slot": "101", - "type": "uint256[49]" - }, - { - "bytes": "32", - "label": "spacer_151_0_32", - "offset": 0, - "slot": "150", - "type": "uint256" - }, - { - "bytes": "1568", - "label": "spacer_152_0_1568", - "offset": 0, - "slot": "151", - "type": "uint256[49]" - }, - { - "bytes": "32", - "label": "spacer_201_0_32", - "offset": 0, - "slot": "200", - "type": "mapping(bytes32 => bool)" - }, - { - "bytes": "32", - "label": "spacer_202_0_32", - "offset": 0, - "slot": "201", - "type": "mapping(bytes32 => bool)" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json index 2928d2147b5c..be59f51e2f9a 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json @@ -75,5 +75,12 @@ "offset": 0, "slot": "7", "type": "uint256" + }, + { + "bytes": "1", + "label": "isIsthmus", + "offset": 0, + "slot": "8", + "type": "bool" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json index 14ee2ff9609a..39a149f0d6e2 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json @@ -76,11 +76,18 @@ "slot": "7", "type": "uint256" }, + { + "bytes": "1", + "label": "isIsthmus", + "offset": 0, + "slot": "8", + "type": "bool" + }, { "bytes": "64", "label": "dependencySet", "offset": 0, - "slot": "8", + "slot": "9", "type": "struct EnumerableSet.UintSet" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json index c68ec541baba..ec29a4be2bfe 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json @@ -7,18 +7,11 @@ "type": "address" }, { - "bytes": "1", - "label": "_initialized", + "bytes": "12", + "label": "spacer_0_20_12", "offset": 20, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 21, - "slot": "0", - "type": "bool" + "type": "bytes12" }, { "bytes": "1600", @@ -92,10 +85,10 @@ }, { "bytes": "20", - "label": "xDomainMsgSender", + "label": "spacer_204_0_20", "offset": 0, "slot": "204", - "type": "address" + "type": "bytes20" }, { "bytes": "30", @@ -113,17 +106,52 @@ }, { "bytes": "20", - "label": "otherMessenger", + "label": "spacer_207_0_20", "offset": 0, "slot": "207", - "type": "contract CrossDomainMessenger" + "type": "address" }, { - "bytes": "1376", - "label": "__gap", + "bytes": "20", + "label": "xDomainMsgSender", "offset": 0, "slot": "208", - "type": "uint256[43]" + "type": "address" + }, + { + "bytes": "12", + "label": "spacer_208_20_12", + "offset": 20, + "slot": "208", + "type": "bytes12" + }, + { + "bytes": "1312", + "label": "__gap", + "offset": 0, + "slot": "209", + "type": "uint256[41]" + }, + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "250", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "250", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_250_2_30", + "offset": 2, + "slot": "250", + "type": "bytes30" }, { "bytes": "20", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json index 2c14ad25904b..cea792454f7b 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json @@ -1,38 +1,24 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" + "type": "bytes32" }, { "bytes": "20", - "label": "messenger", + "label": "spacer_1_0_20", "offset": 0, "slot": "1", - "type": "contract ICrossDomainMessenger" + "type": "address" }, { "bytes": "20", - "label": "otherBridge", + "label": "spacer_2_0_20", "offset": 0, "slot": "2", - "type": "contract ERC721Bridge" + "type": "address" }, { "bytes": "1472", @@ -54,5 +40,12 @@ "offset": 0, "slot": "50", "type": "contract ISuperchainConfig" + }, + { + "bytes": "20", + "label": "crossDomainMessenger", + "offset": 0, + "slot": "51", + "type": "contract ICrossDomainMessenger" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json similarity index 64% rename from packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json rename to packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json index 94c133d105d2..5bcde5813268 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json @@ -1,31 +1,17 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" + "type": "bytes32" }, { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" - }, - { - "bytes": "20", - "label": "bridge", + "bytes": "32", + "label": "spacer_1_0_32", "offset": 0, "slot": "1", - "type": "address" + "type": "bytes32" }, { "bytes": "32", @@ -40,5 +26,33 @@ "offset": 0, "slot": "3", "type": "uint256[48]" + }, + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "51", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "51", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_51_2_30", + "offset": 2, + "slot": "51", + "type": "bytes30" + }, + { + "bytes": "20", + "label": "standardBridge", + "offset": 0, + "slot": "52", + "type": "address" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json index 5562a214e4fe..99875ccd2c6c 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json @@ -1,24 +1,10 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" + "type": "bytes32" }, { "bytes": "20", @@ -36,24 +22,45 @@ }, { "bytes": "20", - "label": "messenger", + "label": "spacer_3_0_20", "offset": 0, "slot": "3", - "type": "contract ICrossDomainMessenger" + "type": "address" }, { "bytes": "20", - "label": "otherBridge", + "label": "spacer_4_0_20", "offset": 0, "slot": "4", - "type": "contract StandardBridge" + "type": "address" }, { - "bytes": "1440", + "bytes": "1408", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[45]" + "type": "uint256[44]" + }, + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "49", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "49", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_49_2_30", + "offset": 2, + "slot": "49", + "type": "bytes30" }, { "bytes": "20", @@ -68,5 +75,12 @@ "offset": 0, "slot": "51", "type": "contract ISystemConfig" + }, + { + "bytes": "20", + "label": "crossDomainMessenger", + "offset": 0, + "slot": "52", + "type": "contract ICrossDomainMessenger" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json index dec784b5a32e..beae81a1fe17 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json @@ -7,18 +7,11 @@ "type": "address" }, { - "bytes": "1", - "label": "_initialized", + "bytes": "12", + "label": "spacer_0_20_12", "offset": 20, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 21, - "slot": "0", - "type": "bool" + "type": "bytes12" }, { "bytes": "1600", @@ -92,10 +85,10 @@ }, { "bytes": "20", - "label": "xDomainMsgSender", + "label": "spacer_204_0_20", "offset": 0, "slot": "204", - "type": "address" + "type": "bytes20" }, { "bytes": "30", @@ -113,16 +106,30 @@ }, { "bytes": "20", - "label": "otherMessenger", + "label": "spacer_207_0_20", "offset": 0, "slot": "207", - "type": "contract CrossDomainMessenger" + "type": "address" }, { - "bytes": "1376", - "label": "__gap", + "bytes": "20", + "label": "xDomainMsgSender", "offset": 0, "slot": "208", - "type": "uint256[43]" + "type": "address" + }, + { + "bytes": "12", + "label": "spacer_208_20_12", + "offset": 20, + "slot": "208", + "type": "bytes12" + }, + { + "bytes": "1312", + "label": "__gap", + "offset": 0, + "slot": "209", + "type": "uint256[41]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json index 546b37ba6398..ad9709095c33 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json @@ -1,38 +1,24 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" + "type": "bytes32" }, { "bytes": "20", - "label": "messenger", + "label": "spacer_1_0_20", "offset": 0, "slot": "1", - "type": "contract ICrossDomainMessenger" + "type": "address" }, { "bytes": "20", - "label": "otherBridge", + "label": "spacer_2_0_20", "offset": 0, "slot": "2", - "type": "contract ERC721Bridge" + "type": "address" }, { "bytes": "1472", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json new file mode 100644 index 000000000000..75e6d15b1f7d --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json @@ -0,0 +1,30 @@ +[ + { + "bytes": "32", + "label": "spacer_0_0_32", + "offset": 0, + "slot": "0", + "type": "bytes32" + }, + { + "bytes": "32", + "label": "spacer_1_0_32", + "offset": 0, + "slot": "1", + "type": "bytes32" + }, + { + "bytes": "32", + "label": "deployments", + "offset": 0, + "slot": "2", + "type": "mapping(address => address)" + }, + { + "bytes": "1536", + "label": "__gap", + "offset": 0, + "slot": "3", + "type": "uint256[48]" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json new file mode 100644 index 000000000000..a0b6f46bf85e --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json @@ -0,0 +1,37 @@ +[ + { + "bytes": "20", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "address" + }, + { + "bytes": "32", + "label": "proxyType", + "offset": 0, + "slot": "1", + "type": "mapping(address => enum ProxyAdmin.ProxyType)" + }, + { + "bytes": "32", + "label": "implementationName", + "offset": 0, + "slot": "2", + "type": "mapping(address => string)" + }, + { + "bytes": "20", + "label": "addressManager", + "offset": 0, + "slot": "3", + "type": "contract IAddressManager" + }, + { + "bytes": "1", + "label": "upgrading", + "offset": 20, + "slot": "3", + "type": "bool" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json index c6ccc0fc2e03..bda39d85dc77 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json @@ -1,24 +1,10 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" + "type": "bytes32" }, { "bytes": "20", @@ -36,23 +22,23 @@ }, { "bytes": "20", - "label": "messenger", + "label": "spacer_3_0_20", "offset": 0, "slot": "3", - "type": "contract ICrossDomainMessenger" + "type": "address" }, { "bytes": "20", - "label": "otherBridge", + "label": "spacer_4_0_20", "offset": 0, "slot": "4", - "type": "contract StandardBridge" + "type": "address" }, { - "bytes": "1440", + "bytes": "1408", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[45]" + "type": "uint256[44]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json index c6ccc0fc2e03..bda39d85dc77 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json @@ -1,24 +1,10 @@ [ { - "bytes": "1", - "label": "_initialized", + "bytes": "32", + "label": "spacer_0_0_32", "offset": 0, "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_0_2_30", - "offset": 2, - "slot": "0", - "type": "bytes30" + "type": "bytes32" }, { "bytes": "20", @@ -36,23 +22,23 @@ }, { "bytes": "20", - "label": "messenger", + "label": "spacer_3_0_20", "offset": 0, "slot": "3", - "type": "contract ICrossDomainMessenger" + "type": "address" }, { "bytes": "20", - "label": "otherBridge", + "label": "spacer_4_0_20", "offset": 0, "slot": "4", - "type": "contract StandardBridge" + "type": "address" }, { - "bytes": "1440", + "bytes": "1408", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[45]" + "type": "uint256[44]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol index 27be4a7332fa..f5a249bfda65 100644 --- a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.15; // Contracts import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -18,7 +19,10 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; /// @notice The L1CrossDomainMessenger is a message passing interface between L1 and L2 responsible /// for sending and receiving data on the L1 side. Users are encouraged to use this /// interface instead of interacting with lower-level contracts directly. -contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver { +contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver, Initializable { + /// @notice Spacer to give the initializer a full slot, to avoid offsetting the superchainConfig slot. + bytes30 private spacer_250_2_30; + /// @notice Contract of the SuperchainConfig. ISuperchainConfig public superchainConfig; @@ -30,16 +34,12 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver { ISystemConfig public systemConfig; /// @notice Semantic version. - /// @custom:semver 2.4.1-beta.2 - string public constant version = "2.4.1-beta.2"; + /// @custom:semver 2.4.1-beta.3 + string public constant version = "2.4.1-beta.3"; /// @notice Constructs the L1CrossDomainMessenger contract. constructor() CrossDomainMessenger() { - initialize({ - _superchainConfig: ISuperchainConfig(address(0)), - _portal: IOptimismPortal(payable(address(0))), - _systemConfig: ISystemConfig(address(0)) - }); + _disableInitializers(); } /// @notice Initializes the contract. @@ -57,7 +57,12 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver { superchainConfig = _superchainConfig; portal = _portal; systemConfig = _systemConfig; - __CrossDomainMessenger_init({ _otherMessenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER) }); + } + + /// @notice Getter function for the other messenger. + /// @return Contract of the messenger on the other network. + function otherMessenger() public pure override returns (CrossDomainMessenger) { + return CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); } /// @inheritdoc CrossDomainMessenger @@ -86,7 +91,7 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver { /// @inheritdoc CrossDomainMessenger function _isOtherMessenger() internal view override returns (bool) { - return msg.sender == address(portal) && portal.l2Sender() == address(otherMessenger); + return msg.sender == address(portal) && portal.l2Sender() == address(otherMessenger()); } /// @inheritdoc CrossDomainMessenger diff --git a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol index bd9b31c1e589..f66c46c67d71 100644 --- a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity 0.8.25; // Contracts import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; +import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -19,7 +20,7 @@ import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; /// @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to /// make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract /// acts as an escrow for ERC721 tokens deposited into L2. -contract L1ERC721Bridge is ERC721Bridge, ISemver { +contract L1ERC721Bridge is ERC721Bridge, Initializable, ISemver { /// @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token /// by ID was deposited for a given L2 token. mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; @@ -27,13 +28,16 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver { /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; + /// @notice Contract of the CrossDomainMessenger on this chain. + ICrossDomainMessenger internal crossDomainMessenger; + /// @notice Semantic version. - /// @custom:semver 2.2.0-beta.1 - string public constant version = "2.2.0-beta.1"; + /// @custom:semver 2.1.1-beta.3 + string public constant version = "2.1.1-beta.3"; /// @notice Constructs the L1ERC721Bridge contract. constructor() ERC721Bridge() { - initialize({ _messenger: ICrossDomainMessenger(address(0)), _superchainConfig: ISuperchainConfig(address(0)) }); + _disableInitializers(); } /// @notice Initializes the contract. @@ -41,7 +45,19 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver { /// @param _superchainConfig Contract of the SuperchainConfig contract on this network. function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) public initializer { superchainConfig = _superchainConfig; - __ERC721Bridge_init({ _messenger: _messenger, _otherBridge: ERC721Bridge(payable(Predeploys.L2_ERC721_BRIDGE)) }); + crossDomainMessenger = _messenger; + } + + /// @notice Getter function for the CrossDomainMessenger contract on this chain. + /// @return Contract of the CrossDomainMessenger on this chain. + function messenger() public view override returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(crossDomainMessenger); + } + + /// @notice Getter function for the other bridge. + /// @return Contract of the bridge on the other network. + function otherBridge() public pure override returns (ERC721Bridge) { + return ERC721Bridge(payable(Predeploys.L2_ERC721_BRIDGE)); } /// @inheritdoc ERC721Bridge @@ -116,7 +132,7 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver { IERC721(_localToken).transferFrom({ from: _from, to: address(this), tokenId: _tokenId }); // Send calldata into L2 - messenger.sendMessage({ _target: address(otherBridge), _message: message, _minGasLimit: _minGasLimit }); + messenger().sendMessage({ _target: address(otherBridge()), _message: message, _minGasLimit: _minGasLimit }); emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); } } diff --git a/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol new file mode 100644 index 000000000000..8d70325597bc --- /dev/null +++ b/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +/// @custom:proxied true +/// @title L1OptimismMintableERC20Factory +/// @notice Allows users to create L1 tokens that represent L2 native tokens. +contract L1OptimismMintableERC20Factory is OptimismMintableERC20Factory, Initializable { + /// @custom:semver 1.3.1-beta.5 + /// @notice Semantic version. + /// The semver MUST be bumped any time that there is a change in + /// the OptimismMintableERC20 token contract since this contract + /// is responsible for deploying OptimismMintableERC20 contracts. + string public constant version = "1.3.1-beta.5"; + + /// @custom:spacer + /// @notice Spacer to fill the remainder of the _initialized slot, preventing the standardBridge + /// address from being packed with it. + bytes30 private spacer_51_2_30; + + /// @notice Address of the bridge on this domain. + address internal standardBridge; + + constructor() { + _disableInitializers(); + } + + /// @notice Initializes the contract. + /// @param _bridge Contract of the bridge on this domain. + function initialize(address _bridge) public initializer { + standardBridge = _bridge; + } + + /// @notice Getter function for the bridge contract. + /// @return Contract of the bridge on this domain. + function bridge() public view virtual override returns (address) { + return standardBridge; + } +} diff --git a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol index 54cc833fe673..f49f29864a93 100644 --- a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol @@ -3,15 +3,16 @@ pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; - // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; /// @custom:proxied true /// @title L1StandardBridge @@ -23,7 +24,7 @@ import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; /// NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples /// of some token types that may not be properly supported by this contract include, but are /// not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists. -contract L1StandardBridge is StandardBridge, ISemver { +contract L1StandardBridge is StandardBridge, ISemver, Initializable { /// @custom:legacy /// @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated. /// @param from Address of the depositor. @@ -75,8 +76,13 @@ contract L1StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 2.2.1-beta.2 - string public constant version = "2.2.1-beta.2"; + /// @custom:semver 2.2.1-beta.3 + string public constant version = "2.2.1-beta.3"; + + /// @custom:spacer + /// @notice Spacer to fill the remainder of the _initialized slot, preventing the superchainConfig + /// address from being packed with it. + bytes30 private spacer_49_2_30; /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; @@ -84,13 +90,12 @@ contract L1StandardBridge is StandardBridge, ISemver { /// @notice Address of the SystemConfig contract. ISystemConfig public systemConfig; + /// @notice Contract for the CrossDomainMessenger on this network. + ICrossDomainMessenger internal crossDomainMessenger; + /// @notice Constructs the L1StandardBridge contract. constructor() StandardBridge() { - initialize({ - _messenger: ICrossDomainMessenger(address(0)), - _superchainConfig: ISuperchainConfig(address(0)), - _systemConfig: ISystemConfig(address(0)) - }); + _disableInitializers(); } /// @notice Initializer. @@ -106,10 +111,19 @@ contract L1StandardBridge is StandardBridge, ISemver { { superchainConfig = _superchainConfig; systemConfig = _systemConfig; - __StandardBridge_init({ - _messenger: _messenger, - _otherBridge: StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)) - }); + crossDomainMessenger = _messenger; + } + + /// @notice Returns the contract of the bridge on the other chain. + /// @return Contract of the bridge on the other chain. + function otherBridge() public pure override returns (IStandardBridge) { + return IStandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)); + } + + /// @notice Getter function for the messenger contract. + /// @return Contract of the messenger on this domain. + function messenger() public view override returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(crossDomainMessenger); } /// @inheritdoc StandardBridge @@ -241,8 +255,8 @@ contract L1StandardBridge is StandardBridge, ISemver { /// @custom:legacy /// @notice Retrieves the access of the corresponding L2 bridge contract. /// @return Address of the corresponding L2 bridge contract. - function l2TokenBridge() external view returns (address) { - return address(otherBridge); + function l2TokenBridge() external pure returns (address) { + return address(otherBridge()); } /// @notice Internal function for initiating an ETH deposit. diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 4bf52ff228a1..721c7649ed0a 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -46,6 +46,7 @@ contract OPContractsManager is ISemver, Initializable { address unsafeBlockSigner; address proposer; address challenger; + address systemConfigFeeAdmin; } /// @notice The full set of inputs to deploy a new OP Stack chain. @@ -129,8 +130,8 @@ contract OPContractsManager is ISemver, Initializable { // -------- Constants and Variables -------- - /// @custom:semver 1.0.0-beta.20 - string public constant version = "1.0.0-beta.20"; + /// @custom:semver 1.0.0-beta.21 + string public constant version = "1.0.0-beta.21"; /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. @@ -254,7 +255,7 @@ contract OPContractsManager is ISemver, Initializable { output.systemConfigProxy = ISystemConfig(deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "SystemConfig")); output.optimismMintableERC20FactoryProxy = IOptimismMintableERC20Factory( - deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "OptimismMintableERC20Factory") + deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "L1OptimismMintableERC20Factory") ); output.disputeGameFactoryProxy = IDisputeGameFactory(deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "DisputeGameFactory")); @@ -468,12 +469,15 @@ contract OPContractsManager is ISemver, Initializable { return abi.encodeWithSelector( _selector, - _input.roles.systemConfigOwner, + ISystemConfig.Roles({ + owner: _input.roles.systemConfigOwner, + feeAdmin: _input.roles.systemConfigFeeAdmin, + unsafeBlockSigner: _input.roles.unsafeBlockSigner, + batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) + }), _input.basefeeScalar, _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, - _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs @@ -487,12 +491,15 @@ contract OPContractsManager is ISemver, Initializable { return abi.encodeWithSelector( _selector, - _input.roles.systemConfigOwner, + ISystemConfig.Roles({ + owner: _input.roles.systemConfigOwner, + feeAdmin: _input.roles.systemConfigFeeAdmin, + unsafeBlockSigner: _input.roles.unsafeBlockSigner, + batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) + }), _input.basefeeScalar, _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, - _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol index 19de5537b41c..a237dc1162f0 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol @@ -41,12 +41,15 @@ contract OPContractsManagerInterop is OPContractsManager { return abi.encodeWithSelector( _selector, - _input.roles.systemConfigOwner, + ISystemConfig.Roles({ + owner: _input.roles.systemConfigOwner, + feeAdmin: _input.roles.systemConfigFeeAdmin, + unsafeBlockSigner: _input.roles.unsafeBlockSigner, + batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) + }), _input.basefeeScalar, _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, - _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs, diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal.sol b/packages/contracts-bedrock/src/L1/OptimismPortal.sol index 2f9f53787581..d097d2e22d12 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal.sol @@ -146,9 +146,9 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { } /// @notice Semantic version. - /// @custom:semver 2.8.1-beta.4 + /// @custom:semver 2.8.1-beta.5 function version() public pure virtual returns (string memory) { - return "2.8.1-beta.4"; + return "2.8.1-beta.5"; } /// @notice Constructs the OptimismPortal contract. @@ -584,17 +584,17 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); } - /// @notice Sets the gas paying token for the L2 system. This token is used as the - /// L2 native asset. Only the SystemConfig contract can call this function. - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { + /// @notice Sets static configuration options for the L2 system. + /// @param _type Type of configuration to set. + /// @param _value Encoded value of the configuration. + function setConfig(Types.ConfigType _type, bytes memory _value) external { if (msg.sender != address(systemConfig)) revert Unauthorized(); // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. + // This value must be large enough to cover the cost of calling `L1Block.setConfig`. useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - // Emit the special deposit transaction directly that sets the gas paying - // token in the L1Block predeploy contract. + // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. emit TransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, @@ -604,7 +604,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { uint256(0), // value uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) + abi.encodeCall(IL1Block.setConfig, (_type, _value)) ) ); } diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 985711ed18e5..951e2bea4a18 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -183,9 +183,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { } /// @notice Semantic version. - /// @custom:semver 3.11.0-beta.6 + /// @custom:semver 3.11.0-beta.7 function version() public pure virtual returns (string memory) { - return "3.11.0-beta.6"; + return "3.11.0-beta.7"; } /// @notice Constructs the OptimismPortal contract. @@ -605,17 +605,17 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); } - /// @notice Sets the gas paying token for the L2 system. This token is used as the - /// L2 native asset. Only the SystemConfig contract can call this function. - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { + /// @notice Sets static configuration options for the L2 system. + /// @param _type Type of configuration to set. + /// @param _value Encoded value of the configuration. + function setConfig(Types.ConfigType _type, bytes memory _value) external { if (msg.sender != address(systemConfig)) revert Unauthorized(); // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. + // This value must be large enough to cover the cost of calling `L1Block.setConfig`. useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - // Emit the special deposit transaction directly that sets the gas paying - // token in the L1Block predeploy contract. + // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. emit TransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, @@ -625,7 +625,30 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { uint256(0), // value uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) + abi.encodeCall(IL1Block.setConfig, (_type, _value)) + ) + ); + } + + /// @notice Calls the L2ProxyAdmin as the DEPOSITOR_ACCOUNT. This function can be used + /// to upgrade the predeploys on L2. Only callable by the upgrader role on the + /// SuperchainConfig. + function upgrade(uint32 _gasLimit, bytes memory _calldata) external { + if (msg.sender != superchainConfig.upgrader()) revert Unauthorized(); + + useGas(_gasLimit); + + // Emit the special deposit transaction which calls to the L2 Proxy Admin + emit TransactionDeposited( + Constants.DEPOSITOR_ACCOUNT, + Predeploys.PROXY_ADMIN, + DEPOSIT_VERSION, + abi.encodePacked( + uint256(0), // mint + uint256(0), // value + uint64(_gasLimit), // gasLimit + false, // isCreation, + _calldata // data ) ); } diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index 4c238c415d34..be23f4eec56d 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -3,12 +3,6 @@ pragma solidity 0.8.15; // Contracts import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; - -// Libraries -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Constants } from "src/libraries/Constants.sol"; -import { Unauthorized } from "src/libraries/PortalErrors.sol"; /// @custom:proxied true /// @title OptimismPortalInterop @@ -23,33 +17,8 @@ contract OptimismPortalInterop is OptimismPortal2 { OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) { } - /// @custom:semver +interop-beta.2 + /// @custom:semver +interop-beta.3 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.2"); - } - - /// @notice Sets static configuration options for the L2 system. - /// @param _type Type of configuration to set. - /// @param _value Encoded value of the configuration. - function setConfig(ConfigType _type, bytes memory _value) external { - if (msg.sender != address(systemConfig)) revert Unauthorized(); - - // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setConfig`. - useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - - // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. - emit TransactionDeposited( - Constants.DEPOSITOR_ACCOUNT, - Predeploys.L1_BLOCK_ATTRIBUTES, - DEPOSIT_VERSION, - abi.encodePacked( - uint256(0), // mint - uint256(0), // value - uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit - false, // isCreation, - abi.encodeCall(L1BlockInterop.setConfig, (_type, _value)) - ) - ); + return string.concat(super.version(), "+interop-beta.3"); } } diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index 51b13936c81b..1701bcff165b 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -12,8 +12,10 @@ import { Storage } from "src/libraries/Storage.sol"; contract SuperchainConfig is Initializable, ISemver { /// @notice Enum representing different types of updates. /// @custom:value GUARDIAN Represents an update to the guardian. + /// @custom:value UPGRADER Represents an update to the upgrader. enum UpdateType { - GUARDIAN + GUARDIAN, + UPGRADER } /// @notice Whether or not the Superchain is paused. @@ -23,6 +25,10 @@ contract SuperchainConfig is Initializable, ISemver { /// It can only be modified by an upgrade. bytes32 public constant GUARDIAN_SLOT = bytes32(uint256(keccak256("superchainConfig.guardian")) - 1); + /// @notice The address of the upgrader, which can faciliate upgrades of L2 predeploys. + /// . It can only be modified by an upgrade. + bytes32 public constant UPGRADER_SLOT = bytes32(uint256(keccak256("superchainConfig.upgrader")) - 1); + /// @notice Emitted when the pause is triggered. /// @param identifier A string helping to identify provenance of the pause transaction. event Paused(string identifier); @@ -36,19 +42,20 @@ contract SuperchainConfig is Initializable, ISemver { event ConfigUpdate(UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 1.1.1-beta.1 - string public constant version = "1.1.1-beta.1"; + /// @custom:semver 1.1.1-beta.2 + string public constant version = "1.1.1-beta.2"; /// @notice Constructs the SuperchainConfig contract. constructor() { - initialize({ _guardian: address(0), _paused: false }); + _disableInitializers(); } /// @notice Initializer. /// @param _guardian Address of the guardian, can pause the OptimismPortal. /// @param _paused Initial paused status. - function initialize(address _guardian, bool _paused) public initializer { + function initialize(address _guardian, address _upgrader, bool _paused) public initializer { _setGuardian(_guardian); + _setUpgrader(_upgrader); if (_paused) { _pause("Initializer paused"); } @@ -59,6 +66,11 @@ contract SuperchainConfig is Initializable, ISemver { guardian_ = Storage.getAddress(GUARDIAN_SLOT); } + /// @notice Getter for the upgrader address. + function upgrader() public view returns (address upgrader_) { + upgrader_ = Storage.getAddress(UPGRADER_SLOT); + } + /// @notice Getter for the current paused status. function paused() public view returns (bool paused_) { paused_ = Storage.getBool(PAUSED_SLOT); @@ -92,4 +104,12 @@ contract SuperchainConfig is Initializable, ISemver { Storage.setAddress(GUARDIAN_SLOT, _guardian); emit ConfigUpdate(UpdateType.GUARDIAN, abi.encode(_guardian)); } + + /// @notice Sets the upgrader address. This is only callable during initialization, so an upgrade + /// will be required to change the upgrader. + /// @param _upgrader The new upgrader address. + function _setUpgrader(address _upgrader) internal { + Storage.setAddress(UPGRADER_SLOT, _upgrader); + emit ConfigUpdate(UpdateType.UPGRADER, abi.encode(_upgrader)); + } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index afb9525403c7..f4b724b8d27e 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -4,15 +4,18 @@ pragma solidity 0.8.15; // Contracts import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { StaticConfig } from "src/libraries/StaticConfig.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; // Libraries import { Storage } from "src/libraries/Storage.sol"; import { Constants } from "src/libraries/Constants.sol"; import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; +import { IOptimismPortal2 as IOptimismPortal } from "src/L1/interfaces/IOptimismPortal2.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; /// @custom:proxied true @@ -48,6 +51,18 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { address gasPayingToken; } + /// @notice Struct representing the roles of the system. + /// @notice The owner (chain operator) of the system. + /// @notice The fee admin of the system. + /// @notice The unsafe block signer of the system. + /// @notice The batcher hash of the system. + struct Roles { + address owner; + address feeAdmin; + address unsafeBlockSigner; + bytes32 batcherHash; + } + /// @notice Version identifier, used for upgrades. uint256 public constant VERSION = 0; @@ -61,6 +76,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// but it is better to be safe than sorry. bytes32 public constant UNSAFE_BLOCK_SIGNER_SLOT = keccak256("systemconfig.unsafeblocksigner"); + /// @notice Storage slot that the feeAdmin address is stored at. + bytes32 internal constant FEE_ADMIN_SLOT = keccak256("systemconfig.feeadmin"); + /// @notice Storage slot that the L1CrossDomainMessenger address is stored at. bytes32 public constant L1_CROSS_DOMAIN_MESSENGER_SLOT = bytes32(uint256(keccak256("systemconfig.l1crossdomainmessenger")) - 1); @@ -137,9 +155,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.3.0-beta.5 + /// @custom:semver 2.3.0-beta.6 function version() public pure virtual returns (string memory) { - return "2.3.0-beta.5"; + return "2.3.0-beta.6"; } /// @notice Constructs the SystemConfig contract. Cannot set @@ -147,55 +165,28 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// implementation, so set it to `address(0xdEaD)` /// @dev START_BLOCK_SLOT is set to type(uint256).max here so that it will be a dead value /// in the singleton and is skipped by initialize when setting the start block. + /// _disableInitializers is called to prevent the need to make calls during the constructor + /// if initialize is called directly. constructor() { Storage.setUint(START_BLOCK_SLOT, type(uint256).max); - initialize({ - _owner: address(0xdEaD), - _basefeeScalar: 0, - _blobbasefeeScalar: 0, - _batcherHash: bytes32(0), - _gasLimit: 1, - _unsafeBlockSigner: address(0), - _config: IResourceMetering.ResourceConfig({ - maxResourceLimit: 1, - elasticityMultiplier: 1, - baseFeeMaxChangeDenominator: 2, - minimumBaseFee: 0, - systemTxMaxGas: 0, - maximumBaseFee: 0 - }), - _batchInbox: address(0), - _addresses: SystemConfig.Addresses({ - l1CrossDomainMessenger: address(0), - l1ERC721Bridge: address(0), - l1StandardBridge: address(0), - disputeGameFactory: address(0), - optimismPortal: address(0), - optimismMintableERC20Factory: address(0), - gasPayingToken: address(0) - }) - }); + _disableInitializers(); } /// @notice Initializer. /// The resource config must be set before the require check. - /// @param _owner Initial owner of the contract. + /// @param _roles Initial roles. /// @param _basefeeScalar Initial basefee scalar value. /// @param _blobbasefeeScalar Initial blobbasefee scalar value. - /// @param _batcherHash Initial batcher hash. /// @param _gasLimit Initial gas limit. - /// @param _unsafeBlockSigner Initial unsafe block signer address. /// @param _config Initial ResourceConfig. /// @param _batchInbox Batch inbox address. An identifier for the op-node to find /// canonical data. /// @param _addresses Set of L1 contract addresses. These should be the proxies. function initialize( - address _owner, + Roles memory _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, - bytes32 _batcherHash, uint64 _gasLimit, - address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, SystemConfig.Addresses memory _addresses @@ -204,29 +195,58 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { initializer { __Ownable_init(); - transferOwnership(_owner); + transferOwnership(_roles.owner); // These are set in ascending order of their UpdateTypes. - _setBatcherHash(_batcherHash); + _setBatcherHash(_roles.batcherHash); _setGasConfigEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar }); _setGasLimit(_gasLimit); - Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner); + Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _roles.unsafeBlockSigner); + Storage.setAddress(FEE_ADMIN_SLOT, _roles.feeAdmin); Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); - Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT, _addresses.l1CrossDomainMessenger); - Storage.setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge); - Storage.setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge); - Storage.setAddress(DISPUTE_GAME_FACTORY_SLOT, _addresses.disputeGameFactory); + Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal); + Storage.setAddress(DISPUTE_GAME_FACTORY_SLOT, _addresses.disputeGameFactory); Storage.setAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT, _addresses.optimismMintableERC20Factory); - _setStartBlock(); _setGasPayingToken(_addresses.gasPayingToken); + _setAddress( + L1_CROSS_DOMAIN_MESSENGER_SLOT, + _addresses.l1CrossDomainMessenger, + Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS + ); + _setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge, Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); + _setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge, Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); + + _setRemoteChainId(); + _setStartBlock(); + + // TODO: set fee vault config calls _setResourceConfig(_config); require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low"); } + /// @notice Internal setter for L1 system addresses that need to be legible from within L2. + /// @param _slot The local storage slot that the address should be stored in. + /// @param _addr The address of the L1 based system address. + /// @param _type The ConfigType that represents what the address is. + function _setAddress(bytes32 _slot, address _addr, Types.ConfigType _type) internal { + Storage.setAddress(_slot, _addr); + IOptimismPortal(payable(optimismPortal())).setConfig({ _type: _type, _value: abi.encode(_addr) }); + } + + /// @notice Internal setter for the base chain's chain id. This allows for the + /// base chain's chain id to be legible from within the parent chain. + /// In the case of an L2, this would be the L1 chain id. + function _setRemoteChainId() internal { + IOptimismPortal(payable(optimismPortal())).setConfig({ + _type: Types.ConfigType.REMOTE_CHAIN_ID, + _value: abi.encode(block.chainid) + }); + } + /// @notice Returns the minimum L2 gas limit that can be safely set for the system to /// operate. The L2 gas limit must be larger than or equal to the amount of /// gas that is allocated for deposits per block plus the amount of gas that @@ -253,6 +273,12 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { addr_ = Storage.getAddress(UNSAFE_BLOCK_SIGNER_SLOT); } + /// @notice High level getter for the fee admin address. + /// @return addr_ Address of the fee admin. + function feeAdmin() public view returns (address addr_) { + addr_ = Storage.getAddress(FEE_ADMIN_SLOT); + } + /// @notice Getter for the L1CrossDomainMessenger address. function l1CrossDomainMessenger() external view returns (address addr_) { addr_ = Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT); @@ -320,7 +346,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// to set the token address. This prevents the token address from being changed /// and makes it explicitly opt-in to use custom gas token. /// @param _token Address of the gas paying token. - function _setGasPayingToken(address _token) internal virtual { + function _setGasPayingToken(address _token) internal { if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) { require( ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token" @@ -328,14 +354,17 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { bytes32 name = GasPayingToken.sanitize(ERC20(_token).name()); bytes32 symbol = GasPayingToken.sanitize(ERC20(_token).symbol()); - // Set the gas paying token in storage and in the OptimismPortal. + // Set the gas paying token in storage and call the OptimismPortal. GasPayingToken.set({ _token: _token, _decimals: GAS_PAYING_TOKEN_DECIMALS, _name: name, _symbol: symbol }); - IOptimismPortal(payable(optimismPortal())).setGasPayingToken({ - _token: _token, - _decimals: GAS_PAYING_TOKEN_DECIMALS, - _name: name, - _symbol: symbol - }); + IOptimismPortal(payable(optimismPortal())).setConfig( + Types.ConfigType.GAS_PAYING_TOKEN, + StaticConfig.encodeSetGasPayingToken({ + _token: _token, + _decimals: GAS_PAYING_TOKEN_DECIMALS, + _name: name, + _symbol: symbol + }) + ); } } @@ -416,6 +445,47 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { _setGasLimit(_gasLimit); } + /// @notice Setter for the FeeVault predeploy configuration. + /// @param _type The FeeVault type. + /// @param _recipient Address that should receive the funds. + /// @param _min Minimum withdrawal amount allowed to be processed. + /// @param _network The network in which the fees should be withdrawn to. + function setFeeVaultConfig( + Types.ConfigType _type, + address _recipient, + uint256 _min, + Types.WithdrawalNetwork _network + ) + external + { + require(msg.sender == feeAdmin(), "SystemConfig: caller is not the fee admin"); + _setFeeVaultConfig(_type, _recipient, _min, _network); + } + + /// @notice Internal function for setting the FeeVault config by type. + /// @param _type The FeeVault type + /// @param _recipient Address that should receive the funds. + /// @param _min Minimum withdrawal amount allowed to be processed. + /// @param _network The network in which the fees should be withdrawn to. + function _setFeeVaultConfig( + Types.ConfigType _type, + address _recipient, + uint256 _min, + Types.WithdrawalNetwork _network + ) + internal + { + require( + _type == Types.ConfigType.BASE_FEE_VAULT_CONFIG || _type == Types.ConfigType.L1_FEE_VAULT_CONFIG + || _type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, + "SystemConfig: ConfigType is is not a Fee Vault Config type" + ); + IOptimismPortal(payable(optimismPortal())).setConfig({ + _type: _type, + _value: abi.encode(Encoding.encodeFeeVaultConfig(_recipient, _min, _network)) + }); + } + /// @notice Internal function for updating the L2 gas limit. /// @param _gasLimit New gas limit. function _setGasLimit(uint64 _gasLimit) internal { @@ -492,6 +562,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { "SystemConfig: precision loss with target resource limit" ); + // TODO: maxResourceLimit must be large enough to handle the SystemConfig.initialize + // call + _resourceConfig = _config; } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 032109286596..755c279fc8ac 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -2,16 +2,13 @@ pragma solidity 0.8.15; // Contracts -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IOptimismPortalInterop as IOptimismPortal } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries -import { Constants } from "src/libraries/Constants.sol"; -import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Storage } from "src/libraries/Storage.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -28,24 +25,20 @@ contract SystemConfigInterop is SystemConfig { 0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c; /// @notice Initializer. - /// @param _owner Initial owner of the contract. + /// @param _roles Initial roles. /// @param _basefeeScalar Initial basefee scalar value. /// @param _blobbasefeeScalar Initial blobbasefee scalar value. - /// @param _batcherHash Initial batcher hash. /// @param _gasLimit Initial gas limit. - /// @param _unsafeBlockSigner Initial unsafe block signer address. /// @param _config Initial ResourceConfig. /// @param _batchInbox Batch inbox address. An identifier for the op-node to find /// canonical data. /// @param _addresses Set of L1 contract addresses. These should be the proxies. /// @param _dependencyManager The addressed allowed to add/remove from the dependency set function initialize( - address _owner, + SystemConfig.Roles memory _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, - bytes32 _batcherHash, uint64 _gasLimit, - address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, SystemConfig.Addresses memory _addresses, @@ -55,12 +48,10 @@ contract SystemConfigInterop is SystemConfig { { // This method has an initializer modifier, and will revert if already initialized. initialize({ - _owner: _owner, + _roles: _roles, _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar, - _batcherHash: _batcherHash, _gasLimit: _gasLimit, - _unsafeBlockSigner: _unsafeBlockSigner, _config: _config, _batchInbox: _batchInbox, _addresses: _addresses @@ -68,38 +59,9 @@ contract SystemConfigInterop is SystemConfig { Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.4 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); - } - - /// @notice Internal setter for the gas paying token address, includes validation. - /// The token must not already be set and must be non zero and not the ether address - /// to set the token address. This prevents the token address from being changed - /// and makes it explicitly opt-in to use custom gas token. Additionally, - /// OptimismPortal's address must be non zero, since otherwise the call to set the - /// config for the gas paying token to OptimismPortal will fail. - /// @param _token Address of the gas paying token. - function _setGasPayingToken(address _token) internal override { - if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) { - require( - ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token" - ); - bytes32 name = GasPayingToken.sanitize(ERC20(_token).name()); - bytes32 symbol = GasPayingToken.sanitize(ERC20(_token).symbol()); - - // Set the gas paying token in storage and in the OptimismPortal. - GasPayingToken.set({ _token: _token, _decimals: GAS_PAYING_TOKEN_DECIMALS, _name: name, _symbol: symbol }); - IOptimismPortal(payable(optimismPortal())).setConfig( - ConfigType.SET_GAS_PAYING_TOKEN, - StaticConfig.encodeSetGasPayingToken({ - _token: _token, - _decimals: GAS_PAYING_TOKEN_DECIMALS, - _name: name, - _symbol: symbol - }) - ); - } + return string.concat(super.version(), "+interop-beta.4"); } /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. @@ -107,7 +69,7 @@ contract SystemConfigInterop is SystemConfig { function addDependency(uint256 _chainId) external { require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); IOptimismPortal(payable(optimismPortal())).setConfig( - ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId) + Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId) ); } @@ -116,7 +78,7 @@ contract SystemConfigInterop is SystemConfig { function removeDependency(uint256 _chainId) external { require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); IOptimismPortal(payable(optimismPortal())).setConfig( - ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId) + Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId) ); } diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol index 8a6de84e2c9d..eb3d0250af2f 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol @@ -7,6 +7,8 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; interface IL1CrossDomainMessenger is ICrossDomainMessenger { + event Initialized(uint8 version); + function PORTAL() external view returns (IOptimismPortal); function initialize( ISuperchainConfig _superchainConfig, @@ -14,6 +16,8 @@ interface IL1CrossDomainMessenger is ICrossDomainMessenger { ISystemConfig _systemConfig ) external; + function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); + function otherMessenger() external pure returns (ICrossDomainMessenger); function portal() external view returns (IOptimismPortal); function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol index 51356bc8d346..ec111614a523 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol @@ -6,6 +6,11 @@ import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMess import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; interface IL1ERC721Bridge is IERC721Bridge { + error InvalidInitialization(); + error NotInitializing(); + + event Initialized(uint64 version); + function bridgeERC721( address _localToken, address _remoteToken, @@ -34,6 +39,7 @@ interface IL1ERC721Bridge is IERC721Bridge { ) external; function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) external; + function otherBridge() external pure returns (IERC721Bridge); function paused() external view returns (bool); function superchainConfig() external view returns (ISuperchainConfig); function version() external view returns (string memory); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol new file mode 100644 index 000000000000..7ac1d3175d40 --- /dev/null +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IL1OptimismMintableERC20Factory { + event Initialized(uint8 version); + event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); + event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); + + function BRIDGE() external view returns (address); + function bridge() external view returns (address); + function createOptimismMintableERC20( + address _remoteToken, + string memory _name, + string memory _symbol + ) + external + returns (address); + function createOptimismMintableERC20WithDecimals( + address _remoteToken, + string memory _name, + string memory _symbol, + uint8 _decimals + ) + external + returns (address); + function createStandardL2Token( + address _remoteToken, + string memory _name, + string memory _symbol + ) + external + returns (address); + function deployments(address) external view returns (address); + function initialize(address _bridge) external; + function version() external view returns (string memory); + + function __constructor__() external; +} diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol index 816436cf1084..53419cdd59d5 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol @@ -25,6 +25,7 @@ interface IL1StandardBridge is IStandardBridge { ); event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); + event Initialized(uint8 version); function depositERC20( address _l1Token, @@ -68,7 +69,9 @@ interface IL1StandardBridge is IStandardBridge { ISystemConfig _systemConfig ) external; - function l2TokenBridge() external view returns (address); + function l2TokenBridge() external pure returns (address); + function OTHER_BRIDGE() external view returns (IStandardBridge); + function otherBridge() external pure returns (IStandardBridge); function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); function version() external view returns (string memory); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol index b9035a6e5143..69f9fb9e16c6 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol @@ -5,6 +5,7 @@ import { Types } from "src/libraries/Types.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol"; +import { Types } from "src/libraries/Types.sol"; interface IOptimismPortal { error BadTarget(); @@ -78,9 +79,9 @@ interface IOptimismPortal { external view returns (bytes32 outputRoot, uint128 timestamp, uint128 l2OutputIndex); // nosemgrep - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); + function setConfig(Types.ConfigType _type, bytes memory _value) external; function version() external pure returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol index 91f09d714314..51395720f283 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol @@ -7,6 +7,7 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { Types } from "src/libraries/Types.sol"; interface IOptimismPortal2 { error AlreadyFinalized(); @@ -108,11 +109,12 @@ interface IOptimismPortal2 { returns (IDisputeGame disputeGameProxy, uint64 timestamp); // nosemgrep function respectedGameType() external view returns (GameType); function respectedGameTypeUpdatedAt() external view returns (uint64); - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; function setRespectedGameType(GameType _gameType) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); function version() external pure returns (string memory); + function setConfig(Types.ConfigType _type, bytes memory _value) external; + function upgrade(uint32 _gasLimit, bytes memory _calldata) external; function __constructor__(uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds) external; } diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol index 521c7232e125..7a66caecbdb7 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol @@ -7,7 +7,6 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; interface IOptimismPortalInterop { error AlreadyFinalized(); @@ -109,11 +108,11 @@ interface IOptimismPortalInterop { returns (IDisputeGame disputeGameProxy, uint64 timestamp); // nosemgrep function respectedGameType() external view returns (GameType); function respectedGameTypeUpdatedAt() external view returns (uint64); - function setConfig(ConfigType _type, bytes memory _value) external; - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; + function setConfig(Types.ConfigType _type, bytes memory _value) external; function setRespectedGameType(GameType _gameType) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); + function upgrade(uint32 _gasLimit, bytes memory _calldata) external; function version() external pure returns (string memory); function __constructor__(uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds) external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol b/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol index dc83893958b0..88f9a7cffae9 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol @@ -13,8 +13,10 @@ interface ISuperchainConfig { function GUARDIAN_SLOT() external view returns (bytes32); function PAUSED_SLOT() external view returns (bytes32); + function UPGRADER_SLOT() external view returns (bytes32); function guardian() external view returns (address guardian_); - function initialize(address _guardian, bool _paused) external; + function upgrader() external view returns (address upgrader_); + function initialize(address _guardian, address _upgrader, bool _paused) external; function pause(string memory _identifier) external; function paused() external view returns (bool paused_); function unpause() external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol index 8959c00b744a..5b68bd7fd099 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol @@ -2,9 +2,12 @@ pragma solidity ^0.8.0; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; +import { Types } from "src/libraries/Types.sol"; /// @notice This interface corresponds to the Custom Gas Token version of the SystemConfig contract. interface ISystemConfig { + error UnsafeCast(); + enum UpdateType { BATCHER, FEE_SCALARS, @@ -23,6 +26,13 @@ interface ISystemConfig { address gasPayingToken; } + struct Roles { + address owner; + address feeAdmin; + address unsafeBlockSigner; + bytes32 batcherHash; + } + event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); event Initialized(uint8 version); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); @@ -49,12 +59,10 @@ interface ISystemConfig { function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); function initialize( - address _owner, + Roles memory _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, - bytes32 _batcherHash, uint64 _gasLimit, - address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, Addresses memory _addresses @@ -70,10 +78,18 @@ interface ISystemConfig { function optimismPortal() external view returns (address addr_); function overhead() external view returns (uint256); function owner() external view returns (address); + function feeAdmin() external view returns (address addr_); function renounceOwnership() external; function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); function scalar() external view returns (uint256); function setBatcherHash(bytes32 _batcherHash) external; + function setFeeVaultConfig( + Types.ConfigType _type, + address _recipient, + uint256 _min, + Types.WithdrawalNetwork _network + ) + external; function setGasConfig(uint256 _overhead, uint256 _scalar) external; function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external; function setGasLimit(uint64 _gasLimit) external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol index e11d17c9f7bd..65eccead56e7 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol @@ -40,6 +40,7 @@ interface ISystemConfigInterop { function optimismPortal() external view returns (address addr_); function overhead() external view returns (uint256); function owner() external view returns (address); + function feeAdmin() external view returns (address); function renounceOwnership() external; function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); function scalar() external view returns (uint256); @@ -57,12 +58,10 @@ interface ISystemConfigInterop { function removeDependency(uint256 _chainId) external; function dependencyManager() external view returns (address); function initialize( - address _owner, + ISystemConfig.Roles memory _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, - bytes32 _batcherHash, uint64 _gasLimit, - address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, ISystemConfig.Addresses memory _addresses, diff --git a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol index 2fd33b9290bf..29710b9eece6 100644 --- a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; - import { Types } from "src/libraries/Types.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000019 @@ -12,18 +12,20 @@ import { Types } from "src/libraries/Types.sol"; /// @notice The BaseFeeVault accumulates the base fee that is paid by transactions. contract BaseFeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; - /// @notice Constructs the BaseFeeVault contract. - /// @param _recipient Wallet that will receive the fees. - /// @param _minWithdrawalAmount Minimum balance for withdrawals. - /// @param _withdrawalNetwork Network which the recipient will receive fees on. - constructor( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) - { } + /// @notice Returns the FeeVault config + /// @return recipient_ Wallet that will receive the fees. + /// @return amount_ Minimum balance for withdrawals. + /// @return withdrawalNetwork_ Network which the recipient will receive fees on. + function config() + public + view + override + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) + { + bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG); + (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); + } } diff --git a/packages/contracts-bedrock/src/L2/FeeVault.sol b/packages/contracts-bedrock/src/L2/FeeVault.sol index 856985d7827b..3d2ecfd6e1a3 100644 --- a/packages/contracts-bedrock/src/L2/FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/FeeVault.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.15; // Libraries import { SafeCall } from "src/libraries/SafeCall.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; // Interfaces import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; @@ -15,27 +16,15 @@ import { Types } from "src/libraries/Types.sol"; /// @notice The FeeVault contract contains the basic logic for the various different vault contracts /// used to hold fee revenue generated by the L2 system. abstract contract FeeVault { - /// @notice Minimum balance before a withdrawal can be triggered. - /// Use the `minWithdrawalAmount()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy - uint256 public immutable MIN_WITHDRAWAL_AMOUNT; - - /// @notice Account that will receive the fees. Can be located on L1 or L2. - /// Use the `recipient()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy - address public immutable RECIPIENT; - - /// @notice Network which the recipient will receive fees on. - /// Use the `withdrawalNetwork()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy - Types.WithdrawalNetwork public immutable WITHDRAWAL_NETWORK; - /// @notice The minimum gas limit for the FeeVault withdrawal transaction. uint32 internal constant WITHDRAWAL_MIN_GAS = 400_000; + /// @notice Internal getter function for the L1Block contract. + /// @return Contract of the L1Block on this domain. + function L1_BLOCK() internal pure returns (IL1Block) { + return IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES); + } + /// @notice Total amount of wei processed by the contract. uint256 public totalProcessed; @@ -56,52 +45,76 @@ abstract contract FeeVault { /// @param withdrawalNetwork Network which the to address will receive funds on. event Withdrawal(uint256 value, address to, address from, Types.WithdrawalNetwork withdrawalNetwork); - /// @param _recipient Wallet that will receive the fees. - /// @param _minWithdrawalAmount Minimum balance for withdrawals. - /// @param _withdrawalNetwork Network which the recipient will receive fees on. - constructor(address _recipient, uint256 _minWithdrawalAmount, Types.WithdrawalNetwork _withdrawalNetwork) { - RECIPIENT = _recipient; - MIN_WITHDRAWAL_AMOUNT = _minWithdrawalAmount; - WITHDRAWAL_NETWORK = _withdrawalNetwork; - } - /// @notice Allow the contract to receive ETH. receive() external payable { } + /// @notice Returns the configuration of the FeeVault. + function config() + public + view + virtual + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); + /// @notice Minimum balance before a withdrawal can be triggered. - function minWithdrawalAmount() public view returns (uint256 amount_) { - amount_ = MIN_WITHDRAWAL_AMOUNT; + function minWithdrawalAmount() public view virtual returns (uint256 amount_) { + (, amount_,) = config(); + } + + /// @notice Minimum balance before a withdrawal can be triggered. + /// Use the `minWithdrawalAmount()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy true + function MIN_WITHDRAWAL_AMOUNT() public view returns (uint256) { + return minWithdrawalAmount(); } /// @notice Account that will receive the fees. Can be located on L1 or L2. - function recipient() public view returns (address recipient_) { - recipient_ = RECIPIENT; + function recipient() public view virtual returns (address recipient_) { + (recipient_,,) = config(); + } + + /// @notice Account that will receive the fees. Can be located on L1 or L2. + /// Use the `recipient()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy + function RECIPIENT() public view returns (address) { + return recipient(); } /// @notice Network which the recipient will receive fees on. - function withdrawalNetwork() public view returns (Types.WithdrawalNetwork network_) { - network_ = WITHDRAWAL_NETWORK; + function withdrawalNetwork() public view returns (Types.WithdrawalNetwork withdrawalNetwork_) { + (,, withdrawalNetwork_) = config(); + } + + /// @notice Network which the recipient will receive fees on. + /// Use the `withdrawalNetwork()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_) { + withdrawalNetwork_ = withdrawalNetwork(); } /// @notice Triggers a withdrawal of funds to the fee wallet on L1 or L2. function withdraw() external { + (address withdrawalRecipient, uint256 withdrawalAmount, Types.WithdrawalNetwork network) = config(); + require( - address(this).balance >= MIN_WITHDRAWAL_AMOUNT, + address(this).balance >= withdrawalAmount, "FeeVault: withdrawal amount must be greater than minimum withdrawal amount" ); uint256 value = address(this).balance; totalProcessed += value; - emit Withdrawal(value, RECIPIENT, msg.sender); - emit Withdrawal(value, RECIPIENT, msg.sender, WITHDRAWAL_NETWORK); + emit Withdrawal(value, withdrawalRecipient, msg.sender); + emit Withdrawal(value, withdrawalRecipient, msg.sender, network); - if (WITHDRAWAL_NETWORK == Types.WithdrawalNetwork.L2) { - bool success = SafeCall.send(RECIPIENT, value); + if (network == Types.WithdrawalNetwork.L2) { + bool success = SafeCall.send(withdrawalRecipient, value); require(success, "FeeVault: failed to send ETH to L2 fee recipient"); } else { IL2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)).initiateWithdrawal{ value: value }({ - _target: RECIPIENT, + _target: withdrawalRecipient, _gasLimit: WITHDRAWAL_MIN_GAS, _data: hex"" }); diff --git a/packages/contracts-bedrock/src/L2/GasPriceOracle.sol b/packages/contracts-bedrock/src/L2/GasPriceOracle.sol index 45f14fe173d0..8b3b71f7c19f 100644 --- a/packages/contracts-bedrock/src/L2/GasPriceOracle.sol +++ b/packages/contracts-bedrock/src/L2/GasPriceOracle.sol @@ -29,8 +29,8 @@ contract GasPriceOracle is ISemver { uint256 public constant DECIMALS = 6; /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.3 - string public constant version = "1.3.1-beta.3"; + /// @custom:semver 1.3.1-beta.4 + string public constant version = "1.3.1-beta.4"; /// @notice This is the intercept value for the linear regression used to estimate the final size of the /// compressed transaction. diff --git a/packages/contracts-bedrock/src/L2/L1Block.sol b/packages/contracts-bedrock/src/L2/L1Block.sol index 0b2ffd2c5782..5a1646bca285 100644 --- a/packages/contracts-bedrock/src/L2/L1Block.sol +++ b/packages/contracts-bedrock/src/L2/L1Block.sol @@ -3,7 +3,17 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { Constants } from "src/libraries/Constants.sol"; +import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol"; +import { IFeeVault, Types as ITypes } from "src/L2/interfaces/IFeeVault.sol"; +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; +import { IERC721Bridge } from "src/universal/interfaces/IERC721Bridge.sol"; +import { IOptimismMintableERC721Factory } from "src/L2/interfaces/IOptimismMintableERC721Factory.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; +import { Storage } from "src/libraries/Storage.sol"; +import { Types } from "src/libraries/Types.sol"; import { NotDepositor } from "src/libraries/L1BlockErrors.sol"; /// @custom:proxied true @@ -17,6 +27,31 @@ contract L1Block is ISemver, IGasToken { /// @notice Event emitted when the gas paying token is set. event GasPayingTokenSet(address indexed token, uint8 indexed decimals, bytes32 name, bytes32 symbol); + /// @notice Storage slot for the base fee vault configuration + bytes32 internal constant BASE_FEE_VAULT_CONFIG_SLOT = bytes32(uint256(keccak256("opstack.basefeevaultconfig")) - 1); + + /// @notice Storage slot for the L1 fee vault configuration + bytes32 internal constant L1_FEE_VAULT_CONFIG_SLOT = bytes32(uint256(keccak256("opstack.l1feevaultconfig")) - 1); + + /// @notice Storage slot for the sequencer fee vault configuration + bytes32 internal constant SEQUENCER_FEE_VAULT_CONFIG_SLOT = + bytes32(uint256(keccak256("opstack.sequencerfeevaultconfig")) - 1); + + /// @notice Storage slot for the L1 cross domain messenger address + bytes32 internal constant L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT = + bytes32(uint256(keccak256("opstack.l1crossdomainmessengeraddress")) - 1); + + /// @notice Storage slot for the L1 ERC721 bridge address + bytes32 internal constant L1_ERC_721_BRIDGE_ADDRESS_SLOT = + bytes32(uint256(keccak256("opstack.l1erc721bridgeaddress")) - 1); + + /// @notice Storage slot for the L1 standard bridge address + bytes32 internal constant L1_STANDARD_BRIDGE_ADDRESS_SLOT = + bytes32(uint256(keccak256("opstack.l1standardbridgeaddress")) - 1); + + /// @notice Storage slot for the remote chain ID + bytes32 internal constant REMOTE_CHAIN_ID_SLOT = bytes32(uint256(keccak256("opstack.remotechainid")) - 1); + /// @notice Address of the special depositor account. function DEPOSITOR_ACCOUNT() public pure returns (address addr_) { addr_ = Constants.DEPOSITOR_ACCOUNT; @@ -57,9 +92,12 @@ contract L1Block is ISemver, IGasToken { /// @notice The latest L1 blob base fee. uint256 public blobBaseFee; - /// @custom:semver 1.5.1-beta.3 + /// @notice Indicates whether the network has gone through the Isthmus upgrade. + bool public isIsthmus; + + /// @custom:semver 1.5.1-beta.4 function version() public pure virtual returns (string memory) { - return "1.5.1-beta.3"; + return "1.5.1-beta.4"; } /// @notice Returns the gas paying token, its decimals, name and symbol. @@ -168,14 +206,117 @@ contract L1Block is ISemver, IGasToken { } } + /// @notice Sets static configuration options for the L2 system. Can only be called by the special + /// depositor account. + /// @param _type The type of configuration to set. + /// @param _value The encoded value with which to set the configuration. + function setConfig(Types.ConfigType _type, bytes calldata _value) public virtual { + if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); + + if (_type == Types.ConfigType.GAS_PAYING_TOKEN) { + (address token, uint8 decimals, bytes32 name, bytes32 symbol) = StaticConfig.decodeSetGasPayingToken(_value); + GasPayingToken.set({ _token: token, _decimals: decimals, _name: name, _symbol: symbol }); + emit GasPayingTokenSet({ token: token, decimals: decimals, name: name, symbol: symbol }); + } else if (_type == Types.ConfigType.BASE_FEE_VAULT_CONFIG) { + Storage.setBytes32(BASE_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); + } else if (_type == Types.ConfigType.L1_FEE_VAULT_CONFIG) { + Storage.setBytes32(L1_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); + } else if (_type == Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS) { + Storage.setAddress(L1_ERC_721_BRIDGE_ADDRESS_SLOT, abi.decode(_value, (address))); + } else if (_type == Types.ConfigType.REMOTE_CHAIN_ID) { + Storage.setUint(REMOTE_CHAIN_ID_SLOT, abi.decode(_value, (uint256))); + } else if (_type == Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS) { + Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT, abi.decode(_value, (address))); + } else if (_type == Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS) { + Storage.setAddress(L1_STANDARD_BRIDGE_ADDRESS_SLOT, abi.decode(_value, (address))); + } else if (_type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG) { + Storage.setBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); + } + } + + /// @notice Returns the configuration value for the given type. All configuration related storage values in + /// the L2 contracts, should be stored in this contract. + /// @param _type The type of configuration to get. + /// @return data_ The encoded configuration value. + function getConfig(Types.ConfigType _type) public view virtual returns (bytes memory data_) { + if (_type == Types.ConfigType.GAS_PAYING_TOKEN) { + (address addr, uint8 decimals) = gasPayingToken(); + data_ = abi.encode( + addr, + decimals, + GasPayingToken.sanitize(gasPayingTokenName()), + GasPayingToken.sanitize(gasPayingTokenSymbol()) + ); + } else if (_type == Types.ConfigType.BASE_FEE_VAULT_CONFIG) { + data_ = abi.encode(Storage.getBytes32(BASE_FEE_VAULT_CONFIG_SLOT)); + } else if (_type == Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS) { + data_ = abi.encode(Storage.getAddress(L1_ERC_721_BRIDGE_ADDRESS_SLOT)); + } else if (_type == Types.ConfigType.REMOTE_CHAIN_ID) { + data_ = abi.encode(Storage.getUint(REMOTE_CHAIN_ID_SLOT)); + } else if (_type == Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS) { + data_ = abi.encode(Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT)); + } else if (_type == Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS) { + data_ = abi.encode(Storage.getAddress(L1_STANDARD_BRIDGE_ADDRESS_SLOT)); + } else if (_type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG) { + data_ = abi.encode(Storage.getBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT)); + } else if (_type == Types.ConfigType.L1_FEE_VAULT_CONFIG) { + data_ = abi.encode(Storage.getBytes32(L1_FEE_VAULT_CONFIG_SLOT)); + } + } + /// @notice Sets the gas paying token for the L2 system. Can only be called by the special - /// depositor account. This function is not called on every L2 block but instead - /// only called by specially crafted L1 deposit transactions. + /// depositor account, initiated by a deposit transaction from L1. + /// This is a legacy setter that exists to give compabilitity with the legacy SystemConfig. + /// Custom gas token can now be set using `setConfig`. This can be removed in the future. function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); GasPayingToken.set({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); - emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); } + + /// @notice Sets the L1 block values for an Isthmus upgraded chain. + /// This function is intended to be called only once, and only on existing chains which are undergoing + /// the Isthmus upgrade. Chains deployed with the Isthmus upgrade activated will have the values set here + /// already populated by the L2 Genesis generation process. + /// In the case of an existing chain underoing the Isthmus upgrade, the expectation is that + /// The upgrade flow will use the following series of Network upgrade automation transactions: + /// 1. Deploy a new `L1BlockImpl` contract. + /// 2. Upgrade only the `L1Block` contract to the new implementation by + /// calling `L2ProxyAdmin.upgrade(address(L1BlockProxy), address(L1BlockImpl))`. + /// 3. Call `L1Block.setIsthmus()` to pull the values from L2 contracts. + /// 4. Upgrades the remainder of the L2 contracts via `L2ProxyAdmin.upgrade()`. + function setIsthmus() external { + if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); + require(isIsthmus == false, "L1Block: Isthmus already active"); + isIsthmus = true; + + Storage.setBytes32(BASE_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.BASE_FEE_VAULT)); + Storage.setBytes32(L1_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.L1_FEE_VAULT)); + Storage.setBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.SEQUENCER_FEE_WALLET)); + + Storage.setAddress( + L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT, + address(ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).otherMessenger()) + ); + Storage.setAddress( + L1_STANDARD_BRIDGE_ADDRESS_SLOT, + address(IStandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).otherBridge()) + ); + Storage.setAddress( + L1_ERC_721_BRIDGE_ADDRESS_SLOT, address(IERC721Bridge(Predeploys.L2_ERC721_BRIDGE).otherBridge()) + ); + Storage.setUint( + REMOTE_CHAIN_ID_SLOT, + IOptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY).remoteChainId() + ); + } + + /// @notice Helper function for migrating deploy config. + function _migrateFeeVaultConfig(address _addr) internal view returns (bytes32) { + address recipient = IFeeVault(payable(_addr)).recipient(); + uint256 amount = IFeeVault(payable(_addr)).minWithdrawalAmount(); + ITypes.WithdrawalNetwork network = IFeeVault(payable(_addr)).withdrawalNetwork(); + return Encoding.encodeFeeVaultConfig(recipient, amount, Types.WithdrawalNetwork(uint8(network))); + } } diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 2cf6bd96c7d4..650799383528 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -6,9 +6,9 @@ import { L1Block } from "src/L2/L1Block.sol"; // Libraries import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; import { NotDepositor, NotCrossL2Inbox, @@ -18,16 +18,6 @@ import { CantRemovedDependency } from "src/libraries/L1BlockErrors.sol"; -/// @notice Enum representing different types of configurations that can be set on L1BlockInterop. -/// @custom:value SET_GAS_PAYING_TOKEN Represents the config type for setting the gas paying token. -/// @custom:value ADD_DEPENDENCY Represents the config type for adding a chain to the interop dependency set. -/// @custom:value REMOVE_DEPENDENCY Represents the config type for removing a chain from the interop dependency set. -enum ConfigType { - SET_GAS_PAYING_TOKEN, - ADD_DEPENDENCY, - REMOVE_DEPENDENCY -} - /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1BlockInterop @@ -49,9 +39,9 @@ contract L1BlockInterop is L1Block { /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300; - /// @custom:semver +interop-beta.1 + /// @custom:semver +interop-beta.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.1"); + return string.concat(super.version(), "+interop-beta.2"); } /// @notice Returns whether the call was triggered from a a deposit or not. @@ -104,28 +94,16 @@ contract L1BlockInterop is L1Block { /// depositor account. /// @param _type The type of configuration to set. /// @param _value The encoded value with which to set the configuration. - function setConfig(ConfigType _type, bytes calldata _value) external { - if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); + function setConfig(Types.ConfigType _type, bytes calldata _value) public override { + super.setConfig(_type, _value); - if (_type == ConfigType.SET_GAS_PAYING_TOKEN) { - _setGasPayingToken(_value); - } else if (_type == ConfigType.ADD_DEPENDENCY) { + if (_type == Types.ConfigType.ADD_DEPENDENCY) { _addDependency(_value); - } else if (_type == ConfigType.REMOVE_DEPENDENCY) { + } else if (_type == Types.ConfigType.REMOVE_DEPENDENCY) { _removeDependency(_value); } } - /// @notice Internal method to set the gas paying token. - /// @param _value The encoded value with which to set the gas paying token. - function _setGasPayingToken(bytes calldata _value) internal { - (address token, uint8 decimals, bytes32 name, bytes32 symbol) = StaticConfig.decodeSetGasPayingToken(_value); - - GasPayingToken.set({ _token: token, _decimals: decimals, _name: name, _symbol: symbol }); - - emit GasPayingTokenSet({ token: token, decimals: decimals, name: name, symbol: symbol }); - } - /// @notice Internal method to add a dependency to the interop dependency set. /// @param _value The encoded value with which to add the dependency. function _addDependency(bytes calldata _value) internal { diff --git a/packages/contracts-bedrock/src/L2/L1FeeVault.sol b/packages/contracts-bedrock/src/L2/L1FeeVault.sol index c80c40b98493..62c04faf3024 100644 --- a/packages/contracts-bedrock/src/L2/L1FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/L1FeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; - import { Types } from "src/libraries/Types.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x420000000000000000000000000000000000001A @@ -12,18 +12,20 @@ import { Types } from "src/libraries/Types.sol"; /// @notice The L1FeeVault accumulates the L1 portion of the transaction fees. contract L1FeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; - /// @notice Constructs the L1FeeVault contract. - /// @param _recipient Wallet that will receive the fees. - /// @param _minWithdrawalAmount Minimum balance for withdrawals. - /// @param _withdrawalNetwork Network which the recipient will receive fees on. - constructor( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) - { } + /// @notice Returns the FeeVault config + /// @return recipient_ Wallet that will receive the fees. + /// @return amount_ Minimum balance for withdrawals. + /// @return withdrawalNetwork_ Network which the recipient will receive fees on. + function config() + public + view + override + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) + { + bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG); + (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); + } } diff --git a/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol index 2461e46d2cf5..d4b19e35f97b 100644 --- a/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol @@ -7,6 +7,7 @@ import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; // Libraries import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -20,26 +21,22 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// L2 on the L2 side. Users are generally encouraged to use this contract instead of lower /// level message passing contracts. contract L2CrossDomainMessenger is CrossDomainMessenger, ISemver { - /// @custom:semver 2.1.1-beta.4 - string public constant version = "2.1.1-beta.4"; - - /// @notice Constructs the L2CrossDomainMessenger contract. - constructor() CrossDomainMessenger() { - initialize({ _l1CrossDomainMessenger: CrossDomainMessenger(address(0)) }); - } - - /// @notice Initializer. - /// @param _l1CrossDomainMessenger L1CrossDomainMessenger contract on the other network. - function initialize(CrossDomainMessenger _l1CrossDomainMessenger) public initializer { - __CrossDomainMessenger_init({ _otherMessenger: _l1CrossDomainMessenger }); + /// @custom:semver 2.1.1-beta.5 + string public constant version = "2.1.1-beta.5"; + + /// @notice Getter for the remote chain's messenger. + function otherMessenger() public view override returns (CrossDomainMessenger) { + bytes memory data = + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS); + return CrossDomainMessenger(abi.decode(data, (address))); } - /// @notice Getter for the remote messenger. + /// @notice Legay getter for the remote chain's messenger. /// Public getter is legacy and will be removed in the future. Use `otherMessenger()` instead. /// @return L1CrossDomainMessenger contract. /// @custom:legacy function l1CrossDomainMessenger() public view returns (CrossDomainMessenger) { - return otherMessenger; + return otherMessenger(); } /// @inheritdoc CrossDomainMessenger @@ -56,7 +53,7 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, ISemver { /// @inheritdoc CrossDomainMessenger function _isOtherMessenger() internal view override returns (bool) { - return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == address(otherMessenger); + return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == address(otherMessenger()); } /// @inheritdoc CrossDomainMessenger diff --git a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol index 85c6856e629d..9536696491ba 100644 --- a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity 0.8.25; // Contracts import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; @@ -7,11 +7,13 @@ import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; // Libraries import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:proxied true @@ -26,21 +28,21 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// wait for the one-week challenge period to elapse before their Optimism-native NFT /// can be refunded on L2. contract L2ERC721Bridge is ERC721Bridge, ISemver { - /// @custom:semver 1.8.0-beta.2 - string public constant version = "1.8.0-beta.2"; + /// @custom:semver 1.8.0-beta.3 + string public constant version = "1.8.0-beta.3"; - /// @notice Constructs the L2ERC721Bridge contract. - constructor() ERC721Bridge() { - initialize({ _l1ERC721Bridge: payable(address(0)) }); + /// @notice Getter function for the messenger contract. + /// @return Address of the messenger on this domain. + function messenger() public pure override returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); } - /// @notice Initializes the contract. - /// @param _l1ERC721Bridge Address of the ERC721 bridge contract on the other network. - function initialize(address payable _l1ERC721Bridge) public initializer { - __ERC721Bridge_init({ - _messenger: ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER), - _otherBridge: ERC721Bridge(_l1ERC721Bridge) - }); + /// @notice Getter function for the other bridge. + /// @return Address of the bridge on the other network. + function otherBridge() public view override returns (ERC721Bridge) { + bytes memory data = + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); + return ERC721Bridge(abi.decode(data, (address))); } /// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the @@ -123,7 +125,7 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver { // Send message to L1 bridge // slither-disable-next-line reentrancy-events - messenger.sendMessage({ _target: address(otherBridge), _message: message, _minGasLimit: _minGasLimit }); + messenger().sendMessage({ _target: address(otherBridge()), _message: message, _minGasLimit: _minGasLimit }); // slither-disable-next-line reentrancy-events emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData); diff --git a/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol new file mode 100644 index 000000000000..dbec47a6d246 --- /dev/null +++ b/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; + +/// @custom:proxied true +/// @custom:predeployed 0x4200000000000000000000000000000000000012 +/// @title L2OptimismMintableERC20Factory +/// @notice L2OptimismMintableERC20Factory is a factory contract that generates OptimismMintableERC20 +/// contracts on the network it's deployed to. Simplifies the deployment process for users +/// who may be less familiar with deploying smart contracts. Designed to be backwards +/// compatible with the older StandardL2ERC20Factory contract. +contract L2OptimismMintableERC20Factory is OptimismMintableERC20Factory { + /// @custom:semver 1.3.1-beta.5 + /// @notice Semantic version. + /// The semver MUST be bumped any time that there is a change in + /// the OptimismMintableERC20 token contract since this contract + /// is responsible for deploying OptimismMintableERC20 contracts. + string public constant version = "1.3.1-beta.5"; + + function bridge() public view virtual override returns (address) { + return Predeploys.L2_STANDARD_BRIDGE; + } +} diff --git a/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol b/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol new file mode 100644 index 000000000000..38178fd51dd2 --- /dev/null +++ b/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; +import { Constants } from "src/libraries/Constants.sol"; + +/// @custom:proxied true +/// @custom:predeploy +/// @title L2ProxyAdmin +contract L2ProxyAdmin is ProxyAdmin { + constructor() ProxyAdmin(Constants.DEPOSITOR_ACCOUNT) { } + + /// @notice The owner of the L2ProxyAdmin is the `DEPOSITOR_ACCOUNT`. + function owner() public pure override returns (address) { + return Constants.DEPOSITOR_ACCOUNT; + } +} diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol index 63bda3209fbb..81b9f825f36a 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol @@ -3,10 +3,12 @@ pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -58,23 +60,21 @@ contract L2StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.11.1-beta.3 + /// @custom:semver 1.11.1-beta.4 function version() public pure virtual returns (string memory) { - return "1.11.1-beta.3"; + return "1.11.1-beta.4"; } - /// @notice Constructs the L2StandardBridge contract. - constructor() StandardBridge() { - initialize({ _otherBridge: StandardBridge(payable(address(0))) }); + /// @notice Returns the corresponding L1 StandardBridge contract. + function otherBridge() public view override returns (IStandardBridge) { + bytes memory data = + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); + return IStandardBridge(abi.decode(data, (address))); } - /// @notice Initializer. - /// @param _otherBridge Contract for the corresponding bridge on the other chain. - function initialize(StandardBridge _otherBridge) public initializer { - __StandardBridge_init({ - _messenger: ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER), - _otherBridge: _otherBridge - }); + /// @notice Returns the cross domain messenger. + function messenger() public pure override returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); } /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. @@ -146,7 +146,7 @@ contract L2StandardBridge is StandardBridge, ISemver { /// @notice Retrieves the access of the corresponding L1 bridge contract. /// @return Address of the corresponding L1 bridge contract. function l1TokenBridge() external view returns (address) { - return address(otherBridge); + return address(otherBridge()); } /// @custom:legacy diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol index e17ef29dd964..622d92187238 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol @@ -40,9 +40,9 @@ contract L2StandardBridgeInterop is L2StandardBridge { event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); /// @notice Semantic version. - /// @custom:semver +interop-beta.2 + /// @custom:semver +interop-beta.3 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.2"); + return string.concat(super.version(), "+interop-beta.3"); } /// @notice Converts `amount` of `from` token to `to` token. diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index 6b1d7327dbc0..7a6342dc07e3 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -65,8 +65,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.10 - string public constant version = "1.0.0-beta.10"; + /// @custom:semver 1.0.0-beta.11 + string public constant version = "1.0.0-beta.11"; /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again. diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol similarity index 51% rename from packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol rename to packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol index 7350e0fae0de..a6818a83c9e6 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol @@ -2,19 +2,18 @@ pragma solidity 0.8.15; import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; +import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; /// @title OptimismMintableERC721Factory /// @notice Factory contract for creating OptimismMintableERC721 contracts. +/// This contract could in theory live on both L1 and L2 but it is not widely +/// used and is therefore set up to work on L2. This could be abstracted in the +/// future to be deployable on L1 as well. contract OptimismMintableERC721Factory is ISemver { - /// @custom:legacy true - /// @notice Address of the ERC721 bridge on this network. - address public immutable BRIDGE; - - /// @custom:legacy true - /// @notice Chain ID for the remote network. - uint256 public immutable REMOTE_CHAIN_ID; - /// @notice Tracks addresses created by this factory. mapping(address => bool) public isOptimismMintableERC721; @@ -25,27 +24,34 @@ contract OptimismMintableERC721Factory is ISemver { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); /// @notice Semantic version. + /// The semver MUST be bumped any time that there is a change in + /// the OptimismMintableERC721 token contract since this contract + /// is responsible for deploying OptimismMintableERC721 contracts. /// @custom:semver 1.4.1-beta.4 string public constant version = "1.4.1-beta.4"; - /// @notice The semver MUST be bumped any time that there is a change in - /// the OptimismMintableERC721 token contract since this contract - /// is responsible for deploying OptimismMintableERC721 contracts. - /// @param _bridge Address of the ERC721 bridge on this network. - /// @param _remoteChainId Chain ID for the remote network. - constructor(address _bridge, uint256 _remoteChainId) { - BRIDGE = _bridge; - REMOTE_CHAIN_ID = _remoteChainId; + /// @notice Returns the remote chain id + function REMOTE_CHAIN_ID() external view returns (uint256) { + return remoteChainId(); + } + + /// @notice Getter function for the remote chain id. + function remoteChainId() public view returns (uint256) { + bytes memory data = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.REMOTE_CHAIN_ID); + return abi.decode(data, (uint256)); } - /// @notice Address of the ERC721 bridge on this network. - function bridge() external view returns (address) { - return BRIDGE; + /// @notice Getter function for the bridge contract. + /// Public getter is legacy and will be removed in the future. Use `bridge()` instead. + /// @return Bridge contract on this domain. + /// @custom:legacy + function BRIDGE() external pure returns (IL2ERC721Bridge) { + return bridge(); } - /// @notice Chain ID for the remote network. - function remoteChainID() external view returns (uint256) { - return REMOTE_CHAIN_ID; + /// @notice Returns the ERC721 bridge contract. + function bridge() public pure returns (IL2ERC721Bridge) { + return IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE); } /// @notice Creates an instance of the standard ERC721. @@ -64,7 +70,7 @@ contract OptimismMintableERC721Factory is ISemver { bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol)); address localToken = - address(new OptimismMintableERC721{ salt: salt }(BRIDGE, REMOTE_CHAIN_ID, _remoteToken, _name, _symbol)); + address(new OptimismMintableERC721{ salt: salt }(bridge(), remoteChainId(), _remoteToken, _name, _symbol)); isOptimismMintableERC721[localToken] = true; emit OptimismMintableERC721Created(localToken, _remoteToken, msg.sender); diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index c323d8b7577b..86f73f1d6f95 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -58,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant override version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.10 + string public constant override version = "1.0.0-beta.10"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol index e2b3dc437b0f..6f79097b16e9 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol @@ -11,8 +11,8 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; /// @notice OptimismSuperchainERC20Beacon is the beacon proxy for the OptimismSuperchainERC20 implementation. contract OptimismSuperchainERC20Beacon is IBeacon, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.2 - string public constant version = "1.0.0-beta.2"; + /// @custom:semver 1.0.0-beta.3 + string public constant version = "1.0.0-beta.3"; /// @inheritdoc IBeacon function implementation() external pure override returns (address) { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol index 454e3b455d62..505bfdc37291 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol @@ -22,8 +22,8 @@ contract OptimismSuperchainERC20Factory is ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.4 - string public constant version = "1.0.0-beta.4"; + /// @custom:semver 1.0.0-beta.5 + string public constant version = "1.0.0-beta.5"; /// @notice Mapping of the deployed OptimismSuperchainERC20 to the remote token address. /// This is used to keep track of the token deployments. diff --git a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol index 69a78219e5bd..965948e42e5f 100644 --- a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; - import { Types } from "src/libraries/Types.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000011 @@ -12,25 +12,27 @@ import { Types } from "src/libraries/Types.sol"; /// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during /// transaction processing and block production. contract SequencerFeeVault is FeeVault, ISemver { - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; - /// @notice Constructs the SequencerFeeVault contract. - /// @param _recipient Wallet that will receive the fees. - /// @param _minWithdrawalAmount Minimum balance for withdrawals. - /// @param _withdrawalNetwork Network which the recipient will receive fees on. - constructor( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) - { } + /// @notice Returns the FeeVault config + /// @return recipient_ Wallet that will receive the fees. + /// @return amount_ Minimum balance for withdrawals. + /// @return withdrawalNetwork_ Network which the recipient will receive fees on. + function config() + public + view + override + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) + { + bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); + (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); + } /// @custom:legacy /// @notice Legacy getter for the recipient address. - /// @return The recipient address. - function l1FeeWallet() public view returns (address) { - return RECIPIENT; + /// @return recipient_ The recipient address. + function l1FeeWallet() public view returns (address recipient_) { + recipient_ = recipient(); } } diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index 29e179eba82c..14d62c02952b 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -24,8 +24,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// do not use a custom gas token. contract SuperchainWETH is WETH98, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.10 - string public constant version = "1.0.0-beta.10"; + /// @custom:semver 1.0.0-beta.11 + string public constant version = "1.0.0-beta.11"; /// @inheritdoc WETH98 function deposit() public payable override { diff --git a/packages/contracts-bedrock/src/L2/WETH.sol b/packages/contracts-bedrock/src/L2/WETH.sol index dacd62c36de9..8580b99e9522 100644 --- a/packages/contracts-bedrock/src/L2/WETH.sol +++ b/packages/contracts-bedrock/src/L2/WETH.sol @@ -14,8 +14,8 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// @title WETH contract that reads the name and symbol from the L1Block contract. /// Allows for nice rendering of token names for chains using custom gas token. contract WETH is WETH98, ISemver { - /// @custom:semver 1.1.0-beta.3 - string public constant version = "1.1.0-beta.3"; + /// @custom:semver 1.1.0-beta.4 + string public constant version = "1.1.0-beta.4"; /// @notice Returns the name of the wrapped native asset. Will be "Wrapped Ether" /// if the native asset is Ether. diff --git a/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol index 7ce731f2e631..a195a2428093 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol @@ -11,19 +11,17 @@ interface IBaseFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); - + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function config() + external + view + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); function version() external view returns (string memory); - function __constructor__( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - external; + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol index 3b4cd0209f13..f970bf61c347 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol @@ -16,12 +16,16 @@ interface IFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function config() + external + view + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol index a43b3c7c3963..2b0a016b3571 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import { Types } from "src/libraries/Types.sol"; + interface IL1Block { error NotDepositor(); + error UnsafeCast(); event GasPayingTokenSet(address indexed token, uint8 indexed decimals, bytes32 name, bytes32 symbol); @@ -17,11 +20,15 @@ interface IL1Block { function gasPayingTokenSymbol() external view returns (string memory symbol_); function hash() external view returns (bytes32); function isCustomGasToken() external view returns (bool); + function isIsthmus() external view returns (bool); function l1FeeOverhead() external view returns (uint256); function l1FeeScalar() external view returns (uint256); function number() external view returns (uint64); function sequenceNumber() external view returns (uint64); + function setConfig(Types.ConfigType _type, bytes memory _value) external; + function getConfig(Types.ConfigType _type) external view returns (bytes memory data_); function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; + function setIsthmus() external; function setL1BlockValues( uint64 _number, uint64 _timestamp, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol index dd72e3fa6f89..bf3be29c0714 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol @@ -1,11 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -enum ConfigType { - SET_GAS_PAYING_TOKEN, - ADD_DEPENDENCY, - REMOVE_DEPENDENCY -} +import { Types } from "src/libraries/Types.sol"; interface IL1BlockInterop { error AlreadyDependency(); @@ -14,6 +10,7 @@ interface IL1BlockInterop { error NotCrossL2Inbox(); error NotDependency(); error NotDepositor(); + error UnsafeCast(); event DependencyAdded(uint256 indexed chainId); event DependencyRemoved(uint256 indexed chainId); @@ -30,16 +27,19 @@ interface IL1BlockInterop { function gasPayingToken() external view returns (address addr_, uint8 decimals_); function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); + function getConfig(Types.ConfigType _type) external view returns (bytes memory data_); function hash() external view returns (bytes32); function isCustomGasToken() external view returns (bool); function isDeposit() external view returns (bool isDeposit_); + function isIsthmus() external view returns (bool); function isInDependencySet(uint256 _chainId) external view returns (bool); function l1FeeOverhead() external view returns (uint256); function l1FeeScalar() external view returns (uint256); function number() external view returns (uint64); function sequenceNumber() external view returns (uint64); - function setConfig(ConfigType _type, bytes memory _value) external; + function setConfig(Types.ConfigType _type, bytes memory _value) external; function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; + function setIsthmus() external; function setL1BlockValues( uint64 _number, uint64 _timestamp, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol index eb695a7e9a58..7f1362a034ab 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol @@ -9,21 +9,20 @@ interface IL1FeeVault { receive() external payable; + function config() + external + view + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function version() external view returns (string memory); - function __constructor__( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - external; + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol index 1cb49f674ec0..60f241d55723 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol @@ -5,8 +5,9 @@ import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMess interface IL2CrossDomainMessenger is ICrossDomainMessenger { function MESSAGE_VERSION() external view returns (uint16); - function initialize(ICrossDomainMessenger _l1CrossDomainMessenger) external; function l1CrossDomainMessenger() external view returns (ICrossDomainMessenger); + function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); + function otherMessenger() external view returns (ICrossDomainMessenger); function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol index a760ce1d803c..6d3c1c1a4d60 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import { IERC721Bridge } from "src/universal/interfaces/IERC721Bridge.sol"; +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; interface IL2ERC721Bridge is IERC721Bridge { function finalizeBridgeERC721( @@ -13,7 +14,7 @@ interface IL2ERC721Bridge is IERC721Bridge { bytes memory _extraData ) external; - function initialize(address payable _l1ERC721Bridge) external; + function messenger() external pure returns (ICrossDomainMessenger); function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol new file mode 100644 index 000000000000..298465021ac6 --- /dev/null +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IL2OptimismMintableERC20Factory { + event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); + event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); + + function BRIDGE() external view returns (address); + function bridge() external view returns (address); + function createOptimismMintableERC20( + address _remoteToken, + string memory _name, + string memory _symbol + ) + external + returns (address); + function createOptimismMintableERC20WithDecimals( + address _remoteToken, + string memory _name, + string memory _symbol, + uint8 _decimals + ) + external + returns (address); + function createStandardL2Token( + address _remoteToken, + string memory _name, + string memory _symbol + ) + external + returns (address); + function deployments(address) external view returns (address); + function version() external view returns (string memory); + + function __constructor__() external; +} diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol index 9f9ce1a85621..3526ae5a49de 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; interface IL2StandardBridge is IStandardBridge { event DepositFinalized( @@ -23,8 +24,13 @@ interface IL2StandardBridge is IStandardBridge { receive() external payable; - function initialize(IStandardBridge _otherBridge) external; function l1TokenBridge() external view returns (address); + function MESSENGER() external view returns (ICrossDomainMessenger); + function messenger() external pure returns (ICrossDomainMessenger); + + function OTHER_BRIDGE() external view returns (IStandardBridge); + function otherBridge() external view returns (IStandardBridge); + function version() external pure returns (string memory); function withdraw( address _l2Token, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol index 6b60f5e4f9b2..aaed62e14f0a 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol @@ -31,8 +31,6 @@ interface IL2StandardBridgeInterop is IStandardBridge { bytes extraData ); - function MESSENGER() external view returns (ICrossDomainMessenger); - function OTHER_BRIDGE() external view returns (IStandardBridge); function bridgeERC20( address _localToken, address _remoteToken, @@ -63,11 +61,15 @@ interface IL2StandardBridgeInterop is IStandardBridge { ) external; function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _extraData) external payable; - function messenger() external view returns (ICrossDomainMessenger); + + function MESSENGER() external view returns (ICrossDomainMessenger); + function messenger() external pure returns (ICrossDomainMessenger); + + function OTHER_BRIDGE() external view returns (IStandardBridge); function otherBridge() external view returns (IStandardBridge); + function paused() external view returns (bool); - function initialize(IStandardBridge _otherBridge) external; function l1TokenBridge() external view returns (address); function withdraw( address _l2Token, diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol similarity index 66% rename from packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol rename to packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol index be3e8ad05425..013c0b73c1a6 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; + interface IOptimismMintableERC721Factory { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); - function BRIDGE() external view returns (address); - function REMOTE_CHAIN_ID() external view returns (uint256); - function bridge() external view returns (address); + function BRIDGE() external pure returns (IL2ERC721Bridge); + function bridge() external pure returns (IL2ERC721Bridge); function createOptimismMintableERC721( address _remoteToken, string memory _name, @@ -15,8 +16,9 @@ interface IOptimismMintableERC721Factory { external returns (address); function isOptimismMintableERC721(address) external view returns (bool); - function remoteChainID() external view returns (uint256); + function REMOTE_CHAIN_ID() external view returns (uint256); + function remoteChainId() external view returns (uint256); function version() external view returns (string memory); - function __constructor__(address _bridge, uint256 _remoteChainId) external; + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol index e4ae46413eff..f6201268134a 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol @@ -11,20 +11,18 @@ interface ISequencerFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); function version() external view returns (string memory); - function l1FeeWallet() external view returns (address); - - function __constructor__( - address _recipient, - uint256 _minWithdrawalAmount, - Types.WithdrawalNetwork _withdrawalNetwork - ) - external; + function l1FeeWallet() external view returns (address recipient_); + function config() + external + view + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 84d5f732f5f6..9d8ffe7b76fb 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -8,6 +8,9 @@ import { RLPWriter } from "src/libraries/rlp/RLPWriter.sol"; /// @title Encoding /// @notice Encoding handles Optimism's various different encoding schemes. library Encoding { + /// @notice Error to be used when an unsafe cast is attempted. + error UnsafeCast(); + /// @notice RLP encodes the L2 transaction that would be generated when a given deposit is sent /// to the L2 system. Useful for searching for a deposit in the L2 system. The /// transaction is prefixed with 0x7e to identify its EIP-2718 type. @@ -136,6 +139,39 @@ library Encoding { return (nonce, version); } + /// @notice Encodes a fee vault configuration. + /// @param _recipient Address of the recipient of the fee vault. + /// @param _amount Amount of the fee vault. + /// @param _network Network of the fee vault. + /// @return Encoded fee vault configuration. + function encodeFeeVaultConfig( + address _recipient, + uint256 _amount, + Types.WithdrawalNetwork _network + ) + internal + pure + returns (bytes32) + { + if (_amount > type(uint88).max) revert UnsafeCast(); + return bytes32(uint256(_network) << 248 | _amount << 160 | uint256(uint160(_recipient))); + } + + /// @notice Decodes a fee vault configuration. + /// @param _data Encoded fee vault configuration. + /// @return recipient_ Recipient of the fee vault. + /// @return amount_ Amount of the fee vault. + /// @return network_ Network of the fee vault. + function decodeFeeVaultConfig(bytes32 _data) + internal + pure + returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork network_) + { + recipient_ = address(uint160(uint256(_data) & uint256(type(uint160).max))); + amount_ = (uint256(_data) & uint256(type(uint88).max) << 160) >> 160; + network_ = Types.WithdrawalNetwork(uint8(uint256(_data >> 248))); + } + /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesEcotone /// @param _baseFeeScalar L1 base fee Scalar /// @param _blobBaseFeeScalar L1 blob base fee Scalar diff --git a/packages/contracts-bedrock/src/libraries/Predeploys.sol b/packages/contracts-bedrock/src/libraries/Predeploys.sol index 30cad3b1f854..7a6b30100b5a 100644 --- a/packages/contracts-bedrock/src/libraries/Predeploys.sol +++ b/packages/contracts-bedrock/src/libraries/Predeploys.sol @@ -119,13 +119,13 @@ library Predeploys { if (_addr == GAS_PRICE_ORACLE) return "GasPriceOracle"; if (_addr == L2_STANDARD_BRIDGE) return "L2StandardBridge"; if (_addr == SEQUENCER_FEE_WALLET) return "SequencerFeeVault"; - if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "OptimismMintableERC20Factory"; + if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "L2OptimismMintableERC20Factory"; if (_addr == L1_BLOCK_NUMBER) return "L1BlockNumber"; if (_addr == L2_ERC721_BRIDGE) return "L2ERC721Bridge"; if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block"; if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser"; if (_addr == OPTIMISM_MINTABLE_ERC721_FACTORY) return "OptimismMintableERC721Factory"; - if (_addr == PROXY_ADMIN) return "ProxyAdmin"; + if (_addr == PROXY_ADMIN) return "L2ProxyAdmin"; if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry"; diff --git a/packages/contracts-bedrock/src/libraries/StaticConfig.sol b/packages/contracts-bedrock/src/libraries/StaticConfig.sol index ffaa0b4e5535..9c16205e281d 100644 --- a/packages/contracts-bedrock/src/libraries/StaticConfig.sol +++ b/packages/contracts-bedrock/src/libraries/StaticConfig.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import { Types } from "src/libraries/Types.sol"; + /// @title StaticConfig /// @notice Library for encoding and decoding static configuration data. library StaticConfig { @@ -57,4 +59,60 @@ library StaticConfig { function decodeRemoveDependency(bytes memory _data) internal pure returns (uint256) { return abi.decode(_data, (uint256)); } + + /// @notice Encodes the static configuration data for setting a fee vault config. + /// @param _recipient Address of the recipient of the fee vault. + /// @param _min Minimum withdrawal amount allowed to be processed. + /// @param _network The network in which the fees should be withdrawn to. + /// @return Encoded static configuration data. + function encodeSetFeeVaultConfig( + address _recipient, + uint256 _min, + Types.WithdrawalNetwork _network + ) + internal + pure + returns (bytes memory) + { + return abi.encode(_recipient, _min, _network); + } + + /// @notice Decodes the static configuration data for setting a fee vault config. + /// @param _data Encoded static configuration data. + /// @return Decoded fee vault config data (recipient, min, network). + function decodeSetFeeVaultConfig(bytes memory _data) + internal + pure + returns (address, uint256, Types.WithdrawalNetwork) + { + return abi.decode(_data, (address, uint256, Types.WithdrawalNetwork)); + } + + /// @notice Encodes the static configuration data for setting an address. + /// @param _address Address to set. + /// @return Encoded static configuration data. + function encodeSetAddress(address _address) internal pure returns (bytes memory) { + return abi.encode(_address); + } + + /// @notice Decodes the static configuration data for setting an address. + /// @param _data Encoded static configuration data. + /// @return Decoded address. + function decodeSetAddress(bytes memory _data) internal pure returns (address) { + return abi.decode(_data, (address)); + } + + /// @notice Encodes the static configuration data for setting a remote chain ID. + /// @param _chainId Chain ID of the remote chain. + /// @return Encoded static configuration data. + function encodeSetRemoteChainId(uint256 _chainId) internal pure returns (bytes memory) { + return abi.encode(_chainId); + } + + /// @notice Decodes the static configuration data for setting a remote chain ID. + /// @param _data Encoded static configuration data. + /// @return Decoded chain ID of the remote chain. + function decodeSetRemoteChainId(bytes memory _data) internal pure returns (uint256) { + return abi.decode(_data, (uint256)); + } } diff --git a/packages/contracts-bedrock/src/libraries/Types.sol b/packages/contracts-bedrock/src/libraries/Types.sol index 7e9a65654bc1..ea1513df3693 100644 --- a/packages/contracts-bedrock/src/libraries/Types.sol +++ b/packages/contracts-bedrock/src/libraries/Types.sol @@ -75,4 +75,29 @@ library Types { L1, L2 } + + /// @notice Enum representing different types of configurations that can be set on L1BlockIsthmus. + /// @custom:value GAS_PAYING_TOKEN Config type for the gas paying token. + /// @custom:value BASE_FEE_VAULT_CONFIG Config type for the base fee vault config. + /// @custom:value L1_FEE_VAULT_CONFIG Config type for the L1 fee vault config. + /// @custom:value SEQUENCER_FEE_VAULT_CONFIG Config type for the sequencer fee vault config. + /// @custom:value L1_CROSS_DOMAIN_MESSENGER_ADDRESS Config type for the L1 Cross Domain Messenger address. + /// @custom:value L1_ERC_721_BRIDGE_ADDRESS Config type for the L1 ERC721 Bridge address. + /// @custom:value L1_STANDARD_BRIDGE_ADDRESS Config type for the L1 Standard Bridge address. + /// @custom:value REMOTE_CHAIN_ID Config type for the remote chain ID. + /// @custom:value ADD_DEPENDENCY Config type for adding a chain to the interop dependency set. + /// @custom:value REMOVE_DEPENDENCY Config type for removing a chain from the interop dependency + /// set. + enum ConfigType { + GAS_PAYING_TOKEN, + BASE_FEE_VAULT_CONFIG, + L1_FEE_VAULT_CONFIG, + SEQUENCER_FEE_VAULT_CONFIG, + L1_CROSS_DOMAIN_MESSENGER_ADDRESS, + L1_ERC_721_BRIDGE_ADDRESS, + L1_STANDARD_BRIDGE_ADDRESS, + REMOTE_CHAIN_ID, + ADD_DEPENDENCY, + REMOVE_DEPENDENCY + } } diff --git a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol index 66c724d3eba9..cf1ba049d428 100644 --- a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol @@ -1,30 +1,27 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Encoding } from "src/libraries/Encoding.sol"; import { Constants } from "src/libraries/Constants.sol"; /// @custom:legacy -/// @title CrossDomainMessengerLegacySpacer0 +/// @title CrossDomainMessengerLegacySpacer /// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the -/// libAddressManager variable used to exist. Must be the first contract in the inheritance -/// tree of the CrossDomainMessenger. -contract CrossDomainMessengerLegacySpacer0 { +/// libAddressManager variable, PausableUpgradable and OwnableUpgradeable +/// variables used to exist. +abstract contract CrossDomainMessengerLegacySpacer { /// @custom:legacy /// @custom:spacer libAddressManager /// @notice Spacer for backwards compatibility. address private spacer_0_0_20; -} -/// @custom:legacy -/// @title CrossDomainMessengerLegacySpacer1 -/// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the -/// PausableUpgradable and OwnableUpgradeable variables used to exist. Must be -/// the third contract in the inheritance tree of the CrossDomainMessenger. -contract CrossDomainMessengerLegacySpacer1 { + /// @custom:legacy + /// @custom:spacer initializer + /// @notice Spacer for backwards compatibility. + bytes12 private spacer_0_20_12; + /// @custom:legacy /// @custom:spacer ContextUpgradable's __gap /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin @@ -34,7 +31,7 @@ contract CrossDomainMessengerLegacySpacer1 { /// @custom:legacy /// @custom:spacer OwnableUpgradeable's _owner /// @notice Spacer for backwards compatibility. - /// Come from OpenZeppelin OwnableUpgradeable. + /// Comes from OpenZeppelin OwnableUpgradeable. address private spacer_51_0_20; /// @custom:legacy @@ -84,11 +81,7 @@ contract CrossDomainMessengerLegacySpacer1 { /// chain it's deployed on. Currently only designed for message passing between two paired /// chains and does not support one-to-many interactions. /// Any changes to this contract MUST result in a semver bump for contracts that inherit it. -abstract contract CrossDomainMessenger is - CrossDomainMessengerLegacySpacer0, - Initializable, - CrossDomainMessengerLegacySpacer1 -{ +abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { /// @notice Current message version identifier. uint16 public constant MESSAGE_VERSION = 1; @@ -119,11 +112,12 @@ abstract contract CrossDomainMessenger is /// can therefore not be relayed again. mapping(bytes32 => bool) public successfulMessages; - /// @notice Address of the sender of the currently executing message on the other chain. If the - /// value of this variable is the default value (0x00000000...dead) then no message is - /// currently being executed. Use the xDomainMessageSender getter which will throw an - /// error if this is the case. - address internal xDomainMsgSender; + /// @custom:legacy + /// @custom:spacer xDomainMsgSender + /// @notice Spacer for backwards compatibility. The storage slot was migrated when the + /// initializer pattern was moved away from in the base contract to remove the + /// need to set `Constants.DEFAULT_L2_SENDER` into storage during a call to `initialize`. + bytes20 internal spacer_204_0_20; /// @notice Nonce for the next message to be sent, without the message version applied. Use the /// messageNonce getter which will insert the message version into the nonce to give you @@ -135,14 +129,23 @@ abstract contract CrossDomainMessenger is /// successfully executed on the first attempt. mapping(bytes32 => bool) public failedMessages; - /// @notice CrossDomainMessenger contract on the other chain. - /// @custom:network-specific - CrossDomainMessenger public otherMessenger; + /// @custom:legacy + /// @custom:spacer CrossDomainMessenger + /// @notice Spacer for backwards compatibility. + address private spacer_207_0_20; + + /// @notice Address of the sender of the currently executing message on the other chain. If the + /// value of this variable is address(0) then no message is currently being executed. + /// Use the xDomainMessageSender getter which will throw an error if this is the case. + address private xDomainMsgSender; + + /// @notice Spacer to ensure that there is no collision with the xDomainMsgSender slot. + bytes12 private spacer_208_20_12; /// @notice Reserve extra slots in the storage layout for future upgrades. - /// A gap size of 43 was chosen here, so that the first slot used in a child contract + /// A gap size of 40 was chosen here, so that the first slot used in a child contract /// would be 1 plus a multiple of 50. - uint256[43] private __gap; + uint256[41] private __gap; /// @notice Emitted whenever a message is sent to the other chain. /// @param target Address of the recipient of the message. @@ -183,7 +186,7 @@ abstract contract CrossDomainMessenger is // guarantee the property that the call to the target contract will always have at least // the minimum gas limit specified by the user. _sendMessage({ - _to: address(otherMessenger), + _to: address(otherMessenger()), _gasLimit: baseGas(_message, _minGasLimit), _value: msg.value, _data: abi.encodeWithSelector( @@ -266,7 +269,7 @@ abstract contract CrossDomainMessenger is // is being re-entered. This marks the message as failed to allow it to be replayed. if ( !SafeCall.hasMinGas(_minGasLimit, RELAY_RESERVED_GAS + RELAY_GAS_CHECK_BUFFER) - || xDomainMsgSender != Constants.DEFAULT_L2_SENDER + || getCrossDomainMessageSender() != Constants.DEFAULT_L2_SENDER ) { failedMessages[versionedHash] = true; emit FailedRelayedMessage(versionedHash); @@ -283,9 +286,9 @@ abstract contract CrossDomainMessenger is return; } - xDomainMsgSender = _sender; + setCrossDomainMessageSender(_sender); bool success = SafeCall.call(_target, gasleft() - RELAY_RESERVED_GAS, _value, _message); - xDomainMsgSender = Constants.DEFAULT_L2_SENDER; + setCrossDomainMessageSender(address(0)); if (success) { // This check is identical to one above, but it ensures that the same message cannot be relayed @@ -308,24 +311,40 @@ abstract contract CrossDomainMessenger is } } + /// @notice Retrieves the address of the contract or wallet that initiated the currently + /// executing message on the other chain. + /// @return Address of the sender of the currently executing message on the other chain. + function getCrossDomainMessageSender() internal view returns (address) { + if (xDomainMsgSender == address(0)) return Constants.DEFAULT_L2_SENDER; + return xDomainMsgSender; + } + + /// @notice Setter function for the cross-domain message sender. + /// @param _address Address of the sender of the currently executing message on the other chain. + function setCrossDomainMessageSender(address _address) internal { + xDomainMsgSender = _address; + } + /// @notice Retrieves the address of the contract or wallet that initiated the currently /// executing message on the other chain. Will throw an error if there is no message /// currently being executed. Allows the recipient of a call to see who triggered it. /// @return Address of the sender of the currently executing message on the other chain. function xDomainMessageSender() external view returns (address) { - require( - xDomainMsgSender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set" - ); - - return xDomainMsgSender; + address sender = getCrossDomainMessageSender(); + require(sender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set"); + return sender; } + /// @notice Returns the contract of the other messenger on this chain. + /// @return Contract of the other messenger on this chain. + function otherMessenger() public view virtual returns (CrossDomainMessenger); + /// @notice Retrieves the address of the paired CrossDomainMessenger contract on the other chain /// Public getter is legacy and will be removed in the future. Use `otherMessenger()` instead. /// @return CrossDomainMessenger contract on the other chain. /// @custom:legacy function OTHER_MESSENGER() public view returns (CrossDomainMessenger) { - return otherMessenger; + return otherMessenger(); } /// @notice Retrieves the next message nonce. Message version will be added to the upper two @@ -371,19 +390,6 @@ abstract contract CrossDomainMessenger is return token != Constants.ETHER; } - /// @notice Initializer. - /// @param _otherMessenger CrossDomainMessenger contract on the other chain. - function __CrossDomainMessenger_init(CrossDomainMessenger _otherMessenger) internal onlyInitializing { - // We only want to set the xDomainMsgSender to the default value if it hasn't been initialized yet, - // meaning that this is a fresh contract deployment. - // This prevents resetting the xDomainMsgSender to the default value during an upgrade, which would enable - // a reentrant withdrawal to sandwhich the upgrade replay a withdrawal twice. - if (xDomainMsgSender == address(0)) { - xDomainMsgSender = Constants.DEFAULT_L2_SENDER; - } - otherMessenger = _otherMessenger; - } - /// @notice Sends a low-level message to the other messenger. Needs to be implemented by child /// contracts because the logic for this depends on the network where the messenger is /// being deployed. diff --git a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol index 52217fab713c..60edc5a55b57 100644 --- a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol @@ -1,24 +1,25 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity 0.8.25; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; /// @title ERC721Bridge /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges. -abstract contract ERC721Bridge is Initializable { +abstract contract ERC721Bridge { /// @custom:spacer ERC721Bridge's initializer slot spacing - /// @notice Spacer to avoid packing into the initializer slot - bytes30 private spacer_0_2_30; + /// @notice Spacer for legacy initializable slot + bytes32 private spacer_0_0_32; - /// @notice Messenger contract on this domain. - /// @custom:network-specific - ICrossDomainMessenger public messenger; + /// @custom:legacy + /// @custom:spacer messenger + /// @notice Spacer for backwards compatibility. + address private spacer_1_0_20; - /// @notice Contract of the bridge on the other network. - /// @custom:network-specific - ERC721Bridge public otherBridge; + /// @custom:legacy + /// @custom:spacer otherBridge + /// @notice Spacer for backwards compatibility. + address private spacer_2_0_20; /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. uint256[46] private __gap; @@ -57,41 +58,37 @@ abstract contract ERC721Bridge is Initializable { /// @notice Ensures that the caller is a cross-chain message from the other bridge. modifier onlyOtherBridge() { + ICrossDomainMessenger crossDomainMessenger = messenger(); require( - msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge), + msg.sender == address(crossDomainMessenger) + && crossDomainMessenger.xDomainMessageSender() == address(otherBridge()), "ERC721Bridge: function can only be called from the other bridge" ); _; } - /// @notice Initializer. - /// @param _messenger Contract of the CrossDomainMessenger on this network. - /// @param _otherBridge Contract of the ERC721 bridge on the other network. - function __ERC721Bridge_init( - ICrossDomainMessenger _messenger, - ERC721Bridge _otherBridge - ) - internal - onlyInitializing - { - messenger = _messenger; - otherBridge = _otherBridge; - } + /// @notice Getter function for the messenger contract. + /// @return Messenger contract on this domain. + function messenger() public view virtual returns (ICrossDomainMessenger); /// @notice Legacy getter for messenger contract. /// Public getter is legacy and will be removed in the future. Use `messenger` instead. /// @return Messenger contract on this domain. /// @custom:legacy function MESSENGER() external view returns (ICrossDomainMessenger) { - return messenger; + return messenger(); } + /// @notice Getter function for the other bridge. + /// @return Contract of the bridge on the other network. + function otherBridge() public view virtual returns (ERC721Bridge); + /// @notice Legacy getter for other bridge address. /// Public getter is legacy and will be removed in the future. Use `otherBridge` instead. /// @return Contract of the bridge on the other network. /// @custom:legacy function OTHER_BRIDGE() external view returns (ERC721Bridge) { - return otherBridge; + return otherBridge(); } /// @notice This function should return true if the contract is paused. diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol index d7a9cd3372cd..788ef2b2f546 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol @@ -3,24 +3,21 @@ pragma solidity 0.8.15; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IOptimismERC20Factory } from "src/L2/interfaces/IOptimismERC20Factory.sol"; -/// @custom:proxied true -/// @custom:predeployed 0x4200000000000000000000000000000000000012 /// @title OptimismMintableERC20Factory -/// @notice OptimismMintableERC20Factory is a factory contract that generates OptimismMintableERC20 -/// contracts on the network it's deployed to. Simplifies the deployment process for users -/// who may be less familiar with deploying smart contracts. Designed to be backwards -/// compatible with the older StandardL2ERC20Factory contract. -contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20Factory { +/// @notice OptimismMintableERC20Factory is an abstract factory contract that generates OptimismMintableERC20 +/// contracts on the network it's deployed to. It should be inherited by a child contract that +/// implements the `bridge` getter function. +/// Any changes to this contract MUST result in a semver bump for contracts that inherit it. +abstract contract OptimismMintableERC20Factory is ISemver, IOptimismERC20Factory { /// @custom:spacer OptimismMintableERC20Factory's initializer slot spacing /// @notice Spacer to avoid packing into the initializer slot - bytes30 private spacer_0_2_30; + bytes32 private spacer_0_0_32; - /// @notice Address of the StandardBridge on this chain. - /// @custom:network-specific - address public bridge; + /// @custom:spacer bridge + /// @notice Spacer to avoid packing into the initializer slot + bytes32 private spacer_1_0_32; /// @notice Mapping of local token address to remote token address. /// This is used to keep track of the token deployments. @@ -44,30 +41,14 @@ contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20F /// @param deployer Address of the account that deployed the token. event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); - /// @notice The semver MUST be bumped any time that there is a change in - /// the OptimismMintableERC20 token contract since this contract - /// is responsible for deploying OptimismMintableERC20 contracts. - /// @notice Semantic version. - /// @custom:semver 1.10.1-beta.4 - string public constant version = "1.10.1-beta.4"; - - /// @notice Constructs the OptimismMintableERC20Factory contract. - constructor() { - initialize({ _bridge: address(0) }); - } - - /// @notice Initializes the contract. - /// @param _bridge Address of the StandardBridge on this chain. - function initialize(address _bridge) public initializer { - bridge = _bridge; - } + function bridge() public view virtual returns (address); /// @notice Getter function for the address of the StandardBridge on this chain. /// Public getter is legacy and will be removed in the future. Use `bridge` instead. /// @return Address of the StandardBridge on this chain. /// @custom:legacy function BRIDGE() external view returns (address) { - return bridge; + return bridge(); } /// @custom:legacy @@ -124,7 +105,7 @@ contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20F bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol, _decimals)); address localToken = - address(new OptimismMintableERC20{ salt: salt }(bridge, _remoteToken, _name, _symbol, _decimals)); + address(new OptimismMintableERC20{ salt: salt }(bridge(), _remoteToken, _name, _symbol, _decimals)); deployments[localToken] = _remoteToken; diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol index 9dd05e10d1fe..692e8546b923 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol @@ -5,6 +5,7 @@ import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extension import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; +import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title OptimismMintableERC721 @@ -29,20 +30,20 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { address public immutable REMOTE_TOKEN; /// @notice Address of the ERC721 bridge on this network. - address public immutable BRIDGE; + IL2ERC721Bridge public immutable BRIDGE; /// @notice Base token URI for this token. string public baseTokenURI; /// @notice Modifier that prevents callers other than the bridge from calling the function. modifier onlyBridge() { - require(msg.sender == BRIDGE, "OptimismMintableERC721: only bridge can call this function"); + require(msg.sender == address(BRIDGE), "OptimismMintableERC721: only bridge can call this function"); _; } /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.3 - string public constant version = "1.3.1-beta.3"; + /// @custom:semver 1.3.1-beta.4 + string public constant version = "1.3.1-beta.4"; /// @param _bridge Address of the bridge on this network. /// @param _remoteChainId Chain ID where the remote token is deployed. @@ -50,7 +51,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { /// @param _name ERC721 name. /// @param _symbol ERC721 symbol. constructor( - address _bridge, + IL2ERC721Bridge _bridge, uint256 _remoteChainId, address _remoteToken, string memory _name, @@ -58,7 +59,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { ) ERC721(_name, _symbol) { - require(_bridge != address(0), "OptimismMintableERC721: bridge cannot be address(0)"); + require(address(_bridge) != address(0), "OptimismMintableERC721: bridge cannot be address(0)"); require(_remoteChainId != 0, "OptimismMintableERC721: remote chain id cannot be zero"); require(_remoteToken != address(0), "OptimismMintableERC721: remote token cannot be address(0)"); @@ -90,8 +91,8 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { } /// @notice Address of the ERC721 bridge on this network. - function bridge() external view returns (address) { - return BRIDGE; + function bridge() external view returns (IL2ERC721Bridge) { + return IL2ERC721Bridge(BRIDGE); } /// @notice Mints some token ID for a user, checking first that contract recipients diff --git a/packages/contracts-bedrock/src/universal/StandardBridge.sol b/packages/contracts-bedrock/src/universal/StandardBridge.sol index 57af2247a65a..42b891c169fb 100644 --- a/packages/contracts-bedrock/src/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/StandardBridge.sol @@ -9,8 +9,8 @@ import { SafeCall } from "src/libraries/SafeCall.sol"; import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { Constants } from "src/libraries/Constants.sol"; /// @custom:upgradeable @@ -18,16 +18,16 @@ import { Constants } from "src/libraries/Constants.sol"; /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles /// the core bridging logic, including escrowing tokens that are native to the local chain /// and minting/burning tokens that are native to the remote chain. -abstract contract StandardBridge is Initializable { +abstract contract StandardBridge { using SafeERC20 for IERC20; /// @notice The L2 gas limit set when eth is depoisited using the receive() function. uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000; /// @custom:legacy - /// @custom:spacer messenger + /// @custom:spacer messenger + initializable /// @notice Spacer for backwards compatibility. - bytes30 private spacer_0_2_30; + bytes32 private spacer_0_0_32; /// @custom:legacy /// @custom:spacer l2TokenBridge @@ -37,18 +37,20 @@ abstract contract StandardBridge is Initializable { /// @notice Mapping that stores deposits for a given pair of local and remote tokens. mapping(address => mapping(address => uint256)) public deposits; - /// @notice Messenger contract on this domain. - /// @custom:network-specific - ICrossDomainMessenger public messenger; + /// @custom:legacy + /// @custom:spacer messenger + /// @notice Spacer for backwards compatibility. + address private spacer_3_0_20; - /// @notice Corresponding bridge on the other domain. - /// @custom:network-specific - StandardBridge public otherBridge; + /// @custom:legacy + /// @custom:spacer otherBridge + /// @notice Spacer for backwards compatibility. + address private spacer_4_0_20; /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. - /// A gap size of 45 was chosen here, so that the first slot used in a child contract - /// would be a multiple of 50. - uint256[45] private __gap; + /// The gap size was previously 45, but is now 44, allowing for the initializer slot to be + /// included in the L1StandardBridge contract without breaking its storage layout. + uint256[44] private __gap; /// @notice Emitted when an ETH bridge is initiated to the other chain. /// @param from Address of the sender. @@ -106,27 +108,15 @@ abstract contract StandardBridge is Initializable { /// @notice Ensures that the caller is a cross-chain message from the other bridge. modifier onlyOtherBridge() { + ICrossDomainMessenger crossDomainMessenger = messenger(); require( - msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge), + msg.sender == address(crossDomainMessenger) + && crossDomainMessenger.xDomainMessageSender() == address(otherBridge()), "StandardBridge: function can only be called from the other bridge" ); _; } - /// @notice Initializer. - /// @param _messenger Contract for CrossDomainMessenger on this network. - /// @param _otherBridge Contract for the other StandardBridge contract. - function __StandardBridge_init( - ICrossDomainMessenger _messenger, - StandardBridge _otherBridge - ) - internal - onlyInitializing - { - messenger = _messenger; - otherBridge = _otherBridge; - } - /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. /// Must be implemented by contracts that inherit. receive() external payable virtual; @@ -140,20 +130,28 @@ abstract contract StandardBridge is Initializable { return token != Constants.ETHER; } + /// @notice Returns the contract of the CrossDomainMessenger on this chain. + /// @return Contract of the CrossDomainMessenger on this chain. + function messenger() public view virtual returns (ICrossDomainMessenger); + /// @notice Getter for messenger contract. /// Public getter is legacy and will be removed in the future. Use `messenger` instead. /// @return Contract of the messenger on this domain. /// @custom:legacy function MESSENGER() external view returns (ICrossDomainMessenger) { - return messenger; + return messenger(); } + /// @notice Returns the contract of the bridge on the other chain. + /// @return Contract of the bridge on the other chain. + function otherBridge() public view virtual returns (IStandardBridge); + /// @notice Getter for the other bridge contract. /// Public getter is legacy and will be removed in the future. Use `otherBridge` instead. /// @return Contract of the bridge on the other network. /// @custom:legacy - function OTHER_BRIDGE() external view returns (StandardBridge) { - return otherBridge; + function OTHER_BRIDGE() external view returns (IStandardBridge) { + return otherBridge(); } /// @notice This function should return true if the contract is paused. @@ -256,7 +254,7 @@ abstract contract StandardBridge is Initializable { require(isCustomGasToken() == false, "StandardBridge: cannot bridge ETH with custom gas token"); require(msg.value == _amount, "StandardBridge: amount sent does not match amount required"); require(_to != address(this), "StandardBridge: cannot send to self"); - require(_to != address(messenger), "StandardBridge: cannot send to messenger"); + require(_to != address(messenger()), "StandardBridge: cannot send to messenger"); // Emit the correct events. By default this will be _amount, but child // contracts may override this function in order to emit legacy events as well. @@ -329,8 +327,8 @@ abstract contract StandardBridge is Initializable { // contracts may override this function in order to emit legacy events as well. _emitETHBridgeInitiated(_from, _to, _amount, _extraData); - messenger.sendMessage{ value: _amount }({ - _target: address(otherBridge), + messenger().sendMessage{ value: _amount }({ + _target: address(otherBridge()), _message: abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData), _minGasLimit: _minGasLimit }); @@ -374,8 +372,8 @@ abstract contract StandardBridge is Initializable { // contracts may override this function in order to emit legacy events as well. _emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); - messenger.sendMessage({ - _target: address(otherBridge), + messenger().sendMessage({ + _target: address(otherBridge()), _message: abi.encodeWithSelector( this.finalizeBridgeERC20.selector, // Because this call will be executed on the remote chain, we reverse the order of diff --git a/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol b/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol index 256b09fa56ef..479e8a6ba273 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; interface ICrossDomainMessenger { event FailedRelayedMessage(bytes32 indexed msgHash); - event Initialized(uint8 version); event RelayedMessage(bytes32 indexed msgHash); event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); event SentMessageExtension1(address indexed sender, uint256 value); @@ -13,6 +12,7 @@ interface ICrossDomainMessenger { function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() external view returns (uint64); function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() external view returns (uint64); function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); + function otherMessenger() external view returns (ICrossDomainMessenger); function RELAY_CALL_OVERHEAD() external view returns (uint64); function RELAY_CONSTANT_OVERHEAD() external view returns (uint64); function RELAY_GAS_CHECK_BUFFER() external view returns (uint64); @@ -20,7 +20,6 @@ interface ICrossDomainMessenger { function baseGas(bytes memory _message, uint32 _minGasLimit) external pure returns (uint64); function failedMessages(bytes32) external view returns (bool); function messageNonce() external view returns (uint256); - function otherMessenger() external view returns (ICrossDomainMessenger); function paused() external view returns (bool); function relayMessage( uint256 _nonce, diff --git a/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol b/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol index 3c97958c1033..fb1f0021aa05 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol @@ -20,7 +20,6 @@ interface IERC721Bridge { uint256 tokenId, bytes extraData ); - event Initialized(uint8 version); function MESSENGER() external view returns (ICrossDomainMessenger); function OTHER_BRIDGE() external view returns (IERC721Bridge); diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol index 91f6eba6c175..36978feece9e 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; interface IOptimismMintableERC20Factory { - event Initialized(uint8 version); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); @@ -31,7 +30,6 @@ interface IOptimismMintableERC20Factory { external returns (address); function deployments(address) external view returns (address); - function initialize(address _bridge) external; function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol index 7d745ad8436e..f678e1338dc5 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; + /// @title IOptimismMintableERC721 /// @notice Interface for contracts that are compatible with the OptimismMintableERC721 standard. /// Tokens that follow this standard can be easily transferred across the ERC721 bridge. interface IOptimismMintableERC721 { function __constructor__( - address _bridge, + IL2ERC721Bridge _bridge, uint256 _remoteChainId, address _remoteToken, string memory _name, @@ -64,11 +66,13 @@ interface IOptimismMintableERC721 { function REMOTE_TOKEN() external view returns (address); - function BRIDGE() external view returns (address); + /// @notice Address of the ERC721 bridge on this network. + function BRIDGE() external view returns (IL2ERC721Bridge); function remoteChainId() external view returns (uint256); function remoteToken() external view returns (address); - function bridge() external view returns (address); + /// @notice Address of the ERC721 bridge on this network. + function bridge() external view returns (IL2ERC721Bridge); } diff --git a/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol b/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol index 406a172c0737..3fdc09df503b 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol @@ -22,12 +22,10 @@ interface IStandardBridge { ); event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); - event Initialized(uint8 version); receive() external payable; function MESSENGER() external view returns (ICrossDomainMessenger); - function OTHER_BRIDGE() external view returns (IStandardBridge); function bridgeERC20( address _localToken, address _remoteToken, @@ -60,6 +58,7 @@ interface IStandardBridge { function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _extraData) external payable; function messenger() external view returns (ICrossDomainMessenger); function otherBridge() external view returns (IStandardBridge); + function OTHER_BRIDGE() external view returns (IStandardBridge); function paused() external view returns (bool); function __constructor__() external; diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 5b2260fce992..232142e9070c 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -70,7 +70,8 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { batcher: _doi.batcher(), unsafeBlockSigner: _doi.unsafeBlockSigner(), proposer: _doi.proposer(), - challenger: _doi.challenger() + challenger: _doi.challenger(), + systemConfigFeeAdmin: msg.sender }), basefeeScalar: _doi.basefeeScalar(), blobBasefeeScalar: _doi.blobBaseFeeScalar(), diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol index 028fc7587043..41c85e463f99 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol @@ -19,6 +19,7 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; +import { StaticConfig } from "src/libraries/StaticConfig.sol"; import "src/libraries/PortalErrors.sol"; // Interfaces @@ -67,6 +68,7 @@ contract OptimismPortal_Test is CommonTest { assertEq(optimismPortal.paused(), false); (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal.params(); assertEq(prevBaseFee, 1 gwei); + // someplace upstream there is a deposit tx assertEq(prevBoughtGas, 0); assertEq(prevBlockNum, uint64(block.number)); } @@ -457,6 +459,7 @@ contract OptimismPortal_Test is CommonTest { ) external { + bytes memory data = StaticConfig.encodeSetGasPayingToken(_token, _decimals, _name, _symbol); vm.expectEmit(address(optimismPortal)); emit TransactionDeposited( 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, @@ -467,12 +470,12 @@ contract OptimismPortal_Test is CommonTest { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) + abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) ) ); vm.prank(address(systemConfig)); - optimismPortal.setGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); + optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); } /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` @@ -491,10 +494,13 @@ contract OptimismPortal_Test is CommonTest { bytes32 name = GasPayingToken.sanitize(_name); bytes32 symbol = GasPayingToken.sanitize(_symbol); + bytes memory data = + StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); + vm.recordLogs(); vm.prank(address(systemConfig)); - optimismPortal.setGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); + optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); optimismPortal.depositTransaction({ @@ -502,7 +508,7 @@ contract OptimismPortal_Test is CommonTest { _value: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1Block.setGasPayingToken, (_token, 18, name, symbol)) + _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) }); VmSafe.Log[] memory logs = vm.getRecordedLogs(); @@ -525,7 +531,7 @@ contract OptimismPortal_Test is CommonTest { vm.assume(_caller != address(systemConfig)); vm.prank(_caller); vm.expectRevert(Unauthorized.selector); - optimismPortal.setGasPayingToken({ _token: address(0), _decimals: 0, _name: "", _symbol: "" }); + optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, hex""); } /// @dev Tests that `depositERC20Transaction` reverts when the gas paying token is ether. diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index 8cc56937bd33..a400c3e5de32 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -19,6 +19,7 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; +import { StaticConfig } from "src/libraries/StaticConfig.sol"; import "src/dispute/lib/Types.sol"; import "src/libraries/PortalErrors.sol"; @@ -308,6 +309,13 @@ contract OptimismPortal2_Test is CommonTest { ) external { + bytes memory data = StaticConfig.encodeSetGasPayingToken({ + _token: _token, + _decimals: _decimals, + _name: _name, + _symbol: _symbol + }); + vm.expectEmit(address(optimismPortal2)); emit TransactionDeposited( 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, @@ -318,12 +326,12 @@ contract OptimismPortal2_Test is CommonTest { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) + abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) ) ); vm.prank(address(systemConfig)); - optimismPortal2.setGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); + optimismPortal2.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); } /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` @@ -342,10 +350,12 @@ contract OptimismPortal2_Test is CommonTest { bytes32 name = GasPayingToken.sanitize(_name); bytes32 symbol = GasPayingToken.sanitize(_symbol); + bytes memory data = StaticConfig.encodeSetGasPayingToken(_token, 18, name, symbol); + vm.recordLogs(); vm.prank(address(systemConfig)); - optimismPortal2.setGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); + optimismPortal2.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); optimismPortal2.depositTransaction({ @@ -353,7 +363,70 @@ contract OptimismPortal2_Test is CommonTest { _value: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1Block.setGasPayingToken, (_token, 18, name, symbol)) + _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + }); + + VmSafe.Log[] memory logs = vm.getRecordedLogs(); + assertEq(logs.length, 2); + + VmSafe.Log memory systemPath = logs[0]; + VmSafe.Log memory userPath = logs[1]; + + assertEq(systemPath.topics.length, 4); + assertEq(systemPath.topics.length, userPath.topics.length); + assertEq(systemPath.topics[0], userPath.topics[0]); + assertEq(systemPath.topics[1], userPath.topics[1]); + assertEq(systemPath.topics[2], userPath.topics[2]); + assertEq(systemPath.topics[3], userPath.topics[3]); + assertEq(systemPath.data, userPath.data); + } + + /// @dev Tests that the upgrade function succeeds. + function testFuzz_upgrade_succeeds(uint32 _gasLimit, bytes memory _calldata) external { + address upgrader = superchainConfig.upgrader(); + + vm.expectEmit(address(optimismPortal2)); + emit TransactionDeposited( + 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, + Predeploys.PROXY_ADMIN, + 0, + abi.encodePacked( + uint256(0), // mint + uint256(0), // value + uint64(_gasLimit), // gasLimit + false, // isCreation, + _calldata + ) + ); + + vm.prank(upgrader); + optimismPortal2.upgrade(_gasLimit, _calldata); + } + + /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` + /// code path that manually emits a deposit transaction outside of the + /// `depositTransaction` function. This is a simple differential test. + function test_upgrade_correctEvent_succeeds(uint32 _gasLimit, bytes memory _calldata) external { + vm.assume(_calldata.length <= 120_000); + IResourceMetering.ResourceConfig memory rcfg = systemConfig.resourceConfig(); + _gasLimit = + uint32(bound(_gasLimit, optimismPortal2.minimumGasLimit(uint32(_calldata.length)), rcfg.maxResourceLimit)); + + vm.recordLogs(); + + vm.prank(superchainConfig.upgrader()); + optimismPortal2.upgrade(_gasLimit, _calldata); + + // Advance the block to ensure that the deposit transaction is processed in the next block, so that we + // aren't limited by the resource limit consumed by the previous call. + vm.roll(block.number + 1); + vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); + optimismPortal2.depositTransaction({ + _to: Predeploys.PROXY_ADMIN, + _value: 0, + _gasLimit: uint64(_gasLimit), + _isCreation: false, + _data: _calldata }); VmSafe.Log[] memory logs = vm.getRecordedLogs(); @@ -371,12 +444,41 @@ contract OptimismPortal2_Test is CommonTest { assertEq(systemPath.data, userPath.data); } - /// @dev Tests that the gas paying token cannot be set by a non-system config. - function test_setGasPayingToken_notSystemConfig_fails(address _caller) external { + /// @dev Tests that any config type can be set by the system config. + function testFuzz_setConfig_succeeds(uint8 _configType, bytes calldata _value) public { + // Ensure that _configType is within the range of the ConfigType enum + _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); + + vm.expectEmit(address(optimismPortal2)); + emitTransactionDeposited({ + _from: Constants.DEPOSITOR_ACCOUNT, + _to: Predeploys.L1_BLOCK_ATTRIBUTES, + _value: 0, + _mint: 0, + _gasLimit: 200_000, + _isCreation: false, + _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType(_configType), _value)) + }); + + vm.prank(address(optimismPortal2.systemConfig())); + optimismPortal2.setConfig(Types.ConfigType(_configType), _value); + } + + /// @dev Tests that the gas paying token cannot be set by a non-system config for any config type. + function testFuzz_setConfig_notSystemConfig_fails( + address _caller, + uint8 _configType, + bytes calldata _value + ) + external + { + // Ensure that _configType is within the range of the ConfigType enum + _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); + vm.assume(_caller != address(systemConfig)); vm.prank(_caller); vm.expectRevert(Unauthorized.selector); - optimismPortal2.setGasPayingToken({ _token: address(0), _decimals: 0, _name: "", _symbol: "" }); + optimismPortal2.setConfig(Types.ConfigType(_configType), _value); } /// @dev Tests that `depositERC20Transaction` reverts when the gas paying token is ether. diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol index bc9a980276aa..a8e0344359ed 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol @@ -7,12 +7,13 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Types } from "src/libraries/Types.sol"; import "src/libraries/PortalErrors.sol"; // Target contract dependencies import "src/libraries/PortalErrors.sol"; import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol"; -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; +import { L1BlockInterop } from "src/L2/L1BlockInterop.sol"; // Interfaces import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; @@ -25,31 +26,11 @@ contract OptimismPortalInterop_Test is CommonTest { super.setUp(); } - /// @dev Tests that the config for the gas paying token can be set. - function testFuzz_setConfig_gasPayingToken_succeeds(bytes calldata _value) public { - vm.expectEmit(address(optimismPortal)); - emitTransactionDeposited({ - _from: Constants.DEPOSITOR_ACCOUNT, - _to: Predeploys.L1_BLOCK_ATTRIBUTES, - _value: 0, - _mint: 0, - _gasLimit: 200_000, - _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)) - }); - - vm.prank(address(_optimismPortalInterop().systemConfig())); - _optimismPortalInterop().setConfig(ConfigType.SET_GAS_PAYING_TOKEN, _value); - } - - /// @dev Tests that setting the gas paying token config as not the system config reverts. - function testFuzz_setConfig_gasPayingToken_notSystemConfig_reverts(bytes calldata _value) public { - vm.expectRevert(Unauthorized.selector); - _optimismPortalInterop().setConfig(ConfigType.SET_GAS_PAYING_TOKEN, _value); - } + /// @dev Tests that any config type can be set by the system config. + function testFuzz_setConfig_succeeds(uint8 _configType, bytes calldata _value) public { + // Ensure that _configType is within the range of the ConfigType enum + _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); - /// @dev Tests that the config for adding a dependency can be set. - function testFuzz_setConfig_addDependency_succeeds(bytes calldata _value) public { vm.expectEmit(address(optimismPortal)); emitTransactionDeposited({ _from: Constants.DEPOSITOR_ACCOUNT, @@ -58,40 +39,28 @@ contract OptimismPortalInterop_Test is CommonTest { _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)) + _data: abi.encodeCall(L1BlockInterop.setConfig, (Types.ConfigType(_configType), _value)) }); vm.prank(address(_optimismPortalInterop().systemConfig())); - _optimismPortalInterop().setConfig(ConfigType.ADD_DEPENDENCY, _value); + _optimismPortalInterop().setConfig(Types.ConfigType(_configType), _value); } - /// @dev Tests that setting the add dependency config as not the system config reverts. - function testFuzz_setConfig_addDependency_notSystemConfig_reverts(bytes calldata _value) public { - vm.expectRevert(Unauthorized.selector); - _optimismPortalInterop().setConfig(ConfigType.ADD_DEPENDENCY, _value); - } - - /// @dev Tests that the config for removing a dependency can be set. - function testFuzz_setConfig_removeDependency_succeeds(bytes calldata _value) public { - vm.expectEmit(address(optimismPortal)); - emitTransactionDeposited({ - _from: Constants.DEPOSITOR_ACCOUNT, - _to: Predeploys.L1_BLOCK_ATTRIBUTES, - _value: 0, - _mint: 0, - _gasLimit: 200_000, - _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)) - }); - - vm.prank(address(_optimismPortalInterop().systemConfig())); - _optimismPortalInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, _value); - } + /// @dev Tests that setting any config type as not the system config reverts. + function testFuzz_setConfig_notSystemConfig_reverts( + address _caller, + uint8 _configType, + bytes calldata _value + ) + external + { + // Ensure that _configType is within the range of the ConfigType enum + _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); - /// @dev Tests that setting the remove dependency config as not the system config reverts. - function testFuzz_setConfig_removeDependency_notSystemConfig_reverts(bytes calldata _value) public { + vm.assume(_caller != address(_optimismPortalInterop().systemConfig())); + vm.prank(_caller); vm.expectRevert(Unauthorized.selector); - _optimismPortalInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, _value); + _optimismPortalInterop().setConfig(Types.ConfigType(_configType), _value); } /// @dev Returns the OptimismPortalInterop instance. diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index 2772ec0c2a3c..b7a76cb664bd 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -13,9 +13,10 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SuperchainConfig_Init_Test is CommonTest { /// @dev Tests that initialization sets the correct values. These are defined in CommonTest.sol. - function test_initialize_unpaused_succeeds() external view { + function test_initialize_succeeds() external view { assertFalse(superchainConfig.paused()); assertEq(superchainConfig.guardian(), deploy.cfg().superchainConfigGuardian()); + assertEq(superchainConfig.upgrader(), deploy.cfg().finalSystemOwner()); } /// @dev Tests that it can be intialized as paused. @@ -36,11 +37,15 @@ contract SuperchainConfig_Init_Test is CommonTest { vm.startPrank(alice); newProxy.upgradeToAndCall( address(newImpl), - abi.encodeCall(ISuperchainConfig.initialize, (deploy.cfg().superchainConfigGuardian(), true)) + abi.encodeCall( + ISuperchainConfig.initialize, + (deploy.cfg().superchainConfigGuardian(), deploy.cfg().finalSystemOwner(), true) + ) ); assertTrue(ISuperchainConfig(address(newProxy)).paused()); assertEq(ISuperchainConfig(address(newProxy)).guardian(), deploy.cfg().superchainConfigGuardian()); + assertEq(ISuperchainConfig(address(newProxy)).upgrader(), deploy.cfg().finalSystemOwner()); } } diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index dff0852fb40d..f2a605bfcd33 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.15; // Testing import { CommonTest } from "test/setup/CommonTest.sol"; +import { VmSafe } from "forge-std/Vm.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -11,6 +12,10 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; +import { StaticConfig } from "src/libraries/StaticConfig.sol"; +import { Types } from "src/libraries/Types.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; +import { Bytes } from "src/libraries/Bytes.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -48,18 +53,18 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { /// @dev Tests that constructor sets the correct values. function test_constructor_succeeds() external view { ISystemConfig impl = ISystemConfig(systemConfigImpl); - assertEq(impl.owner(), address(0xdEaD)); + assertEq(impl.owner(), address(0)); assertEq(impl.overhead(), 0); - assertEq(impl.scalar(), uint256(0x01) << 248); + assertEq(impl.scalar(), 0); assertEq(impl.batcherHash(), bytes32(0)); - assertEq(impl.gasLimit(), 1); + assertEq(impl.gasLimit(), 0); assertEq(impl.unsafeBlockSigner(), address(0)); assertEq(impl.basefeeScalar(), 0); assertEq(impl.blobbasefeeScalar(), 0); IResourceMetering.ResourceConfig memory actual = impl.resourceConfig(); - assertEq(actual.maxResourceLimit, 1); - assertEq(actual.elasticityMultiplier, 1); - assertEq(actual.baseFeeMaxChangeDenominator, 2); + assertEq(actual.maxResourceLimit, 0); + assertEq(actual.elasticityMultiplier, 0); + assertEq(actual.baseFeeMaxChangeDenominator, 0); assertEq(actual.minimumBaseFee, 0); assertEq(actual.systemTxMaxGas, 0); assertEq(actual.maximumBaseFee, 0); @@ -78,6 +83,7 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { assertEq(decimals, 18); } + // TODO: add a reinit test to ensure calls are made to the Portal. /// @dev Tests that initialization sets the correct values. function test_initialize_succeeds() external view { assertEq(systemConfig.owner(), owner); @@ -113,6 +119,54 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { assertEq(token, Constants.ETHER); assertEq(decimals, 18); } + + /// @dev Tests that the gas usage of `initialize` does not exceed the max resource limit. + function test_initialize_gasUsage() external { + // Wipe out the initialized slot so the proxy can be initialized again + vm.store(address(systemConfig), bytes32(0), bytes32(0)); + + vm.recordLogs(); + systemConfig.initialize({ + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), + _basefeeScalar: basefeeScalar, + _blobbasefeeScalar: blobbasefeeScalar, + _gasLimit: gasLimit, + _config: Constants.DEFAULT_RESOURCE_CONFIG(), + _batchInbox: address(0), + _addresses: ISystemConfig.Addresses({ + l1CrossDomainMessenger: address(0), + l1ERC721Bridge: address(0), + l1StandardBridge: address(0), + disputeGameFactory: address(0), + optimismPortal: address(optimismPortal), + optimismMintableERC20Factory: address(0), + gasPayingToken: Constants.ETHER + }) + }); + + VmSafe.Log[] memory logs = vm.getRecordedLogs(); + uint64 totalGasUsed = 0; + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].topics[0] == keccak256("TransactionDeposited(address,address,uint256,bytes)")) { + // The first 32 bytes of the Data will give us the offset of the opaqueData, + // The next 32 bytes indicate the length of the opaqueData content. + // The remaining data is the opaqueData which is tightly packed. There are two + // uint256 values before the gasLimit, so we'll start at 4x32 = 128. + uint256 start = 128; + uint64 gasUsed = uint64(bytes8(Bytes.slice(logs[i].data, start, 8))); + // Assert that the expected SYSTEM_DEPOSIT_GAS_LIMIT gas limit is used for the + // detected events. + assertEq(gasUsed, 200_000); + totalGasUsed += gasUsed; + } + } + assertLe(totalGasUsed, Constants.DEFAULT_RESOURCE_CONFIG().maxResourceLimit); + } } contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { @@ -128,12 +182,15 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { vm.expectRevert("SystemConfig: gas limit too low"); systemConfig.initialize({ - _owner: alice, + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, - _batcherHash: bytes32(hex"abcd"), _gasLimit: minimumGasLimit - 1, - _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -141,7 +198,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(0), + optimismPortal: address(optimismPortal), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -158,12 +215,15 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { // Initialize and check that StartBlock updates to current block number vm.prank(systemConfig.owner()); systemConfig.initialize({ - _owner: alice, + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, - _batcherHash: bytes32(hex"abcd"), _gasLimit: gasLimit, - _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -171,7 +231,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(0), + optimismPortal: address(optimismPortal), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -189,12 +249,15 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { // Initialize and check that StartBlock doesn't update vm.prank(systemConfig.owner()); systemConfig.initialize({ - _owner: alice, + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, - _batcherHash: bytes32(hex"abcd"), _gasLimit: gasLimit, - _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -202,7 +265,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(0), + optimismPortal: address(optimismPortal), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -284,12 +347,15 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init { vm.expectRevert(bytes(revertMessage)); systemConfig.initialize({ - _owner: address(0xdEaD), + _roles: ISystemConfig.Roles({ + owner: address(0xdEaD), + feeAdmin: address(0xdEaD), + unsafeBlockSigner: address(1), + batcherHash: bytes32(0) + }), _basefeeScalar: 0, _blobbasefeeScalar: 0, - _batcherHash: bytes32(0), _gasLimit: gasLimit, - _unsafeBlockSigner: address(0), _config: config, _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -297,7 +363,7 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(0), + optimismPortal: address(optimismPortal), optimismMintableERC20Factory: address(0), gasPayingToken: address(0) }) @@ -323,12 +389,15 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SYMBOL_SLOT, bytes32(0)); systemConfig.initialize({ - _owner: alice, + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), _basefeeScalar: 2100, _blobbasefeeScalar: 1000000, - _batcherHash: bytes32(hex"abcd"), _gasLimit: 30_000_000, - _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -341,6 +410,13 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { gasPayingToken: _gasPayingToken }) }); + + vm.roll(block.number + 1); + // Reset the OptimismPortal resource config gas used + bytes32 slot = vm.load(address(optimismPortal), bytes32(uint256(1))); + vm.store( + address(optimismPortal), bytes32(uint256(1)), bytes32(uint256(slot) & ~(uint256(type(uint64).max) << 64)) + ); } /// @dev Tests that initialization sets the correct values and getters work. @@ -451,9 +527,15 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { /// @dev Tests that initialization works with OptimismPortal. function test_initialize_customGasTokenCall_succeeds() external { + bytes memory data = StaticConfig.encodeSetGasPayingToken({ + _token: address(token), + _decimals: 18, + _name: bytes32("Silly"), + _symbol: bytes32("SIL") + }); + vm.expectCall( - address(optimismPortal), - abi.encodeCall(optimismPortal.setGasPayingToken, (address(token), 18, bytes32("Silly"), bytes32("SIL"))) + address(optimismPortal), abi.encodeCall(optimismPortal.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) ); vm.expectEmit(address(optimismPortal)); @@ -466,7 +548,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setGasPayingToken, (address(token), 18, bytes32("Silly"), bytes32("SIL"))) + abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) ) ); @@ -527,6 +609,26 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init { systemConfig.setGasLimit(maximumGasLimit + 1); } + /// @dev Tests that `setFeeVaultConfig` reverts if the config type is not a fee vault config. + function testFuzz_setFeeVaultConfig_badType_reverts(uint8 _type) external { + // Ensure that _type is a valid ConfigType, but not a fee vault type. + _type = uint8(bound(uint256(_type), 0, uint256(uint8(type(Types.ConfigType).max)))); + vm.assume(_type == 0 || _type > 3); + + vm.prank(systemConfig.feeAdmin()); + vm.expectRevert("SystemConfig: ConfigType is is not a Fee Vault Config type"); + systemConfig.setFeeVaultConfig(Types.ConfigType(_type), address(0), 0, Types.WithdrawalNetwork.L1); + } + + /// @dev Tests that `setFeeVaultConfig` reverts if the caller is not authorized. + function testFuzz_setFeeVaultConfig_badAuth_reverts(address _caller) external { + vm.assume(_caller != systemConfig.feeAdmin()); + vm.expectRevert("SystemConfig: caller is not the fee admin"); + + vm.prank(_caller); + systemConfig.setFeeVaultConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG, _caller, 0, Types.WithdrawalNetwork.L1); + } + /// @dev Tests that `setEIP1559Params` reverts if the caller is not the owner. function test_setEIP1559Params_notOwner_reverts(uint32 _denominator, uint32 _elasticity) external { vm.expectRevert("Ownable: caller is not the owner"); @@ -615,6 +717,42 @@ contract SystemConfig_Setters_Test is SystemConfig_Init { assertEq(systemConfig.unsafeBlockSigner(), newUnsafeSigner); } + /// @dev Tests that `setFeeVaultConfig` emits the expected event in the OptimismPortal + function testFuzz_setFeeVaultConfig_succeeds( + uint8 _vaultConfig, + address _recipient, + uint256 _min, + uint8 _network + ) + external + { + vm.assume(_min <= type(uint88).max); + // Bound the _vaultConfig to one of the 3 enum entries associated with Fee Vault Config. + Types.ConfigType feeType = Types.ConfigType(uint8(bound(_vaultConfig, 1, 3))); + // Bound the _network to one of the 2 enum entries associated with Withdrawal Network. + Types.WithdrawalNetwork withdrawalNetwork = Types.WithdrawalNetwork(uint8(bound(_network, 0, 1))); + + bytes memory value = abi.encode(Encoding.encodeFeeVaultConfig(_recipient, _min, withdrawalNetwork)); + + address feeAdmin = systemConfig.feeAdmin(); + vm.expectEmit(address(optimismPortal2)); + emit TransactionDeposited( + 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, + Predeploys.L1_BLOCK_ATTRIBUTES, + 0, + abi.encodePacked( + uint256(0), // mint + uint256(0), // value + uint64(200_000), // gasLimit + false, // isCreation, + abi.encodeCall(IL1Block.setConfig, (feeType, value)) + ) + ); + + vm.prank(feeAdmin); + systemConfig.setFeeVaultConfig(feeType, _recipient, _min, withdrawalNetwork); + } + /// @dev Tests that `setEIP1559Params` updates the EIP1559 parameters successfully. function testFuzz_setEIP1559Params_succeeds(uint32 _denominator, uint32 _elasticity) external { vm.assume(_denominator > 1); @@ -631,3 +769,5 @@ contract SystemConfig_Setters_Test is SystemConfig_Init { assertEq(systemConfig.eip1559Elasticity(), _elasticity); } } + +// TODO: GasBenchmarks for initialize diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index 426dba30c72a..f10d175b0d5c 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -6,12 +6,12 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; @@ -50,7 +50,7 @@ contract SystemConfigInterop_Test is CommonTest { abi.encodeCall( IOptimismPortalInterop.setConfig, ( - ConfigType.SET_GAS_PAYING_TOKEN, + Types.ConfigType.GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: 18, @@ -70,7 +70,7 @@ contract SystemConfigInterop_Test is CommonTest { address(optimismPortal), abi.encodeCall( IOptimismPortalInterop.setConfig, - (ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)) + (Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)) ) ); @@ -92,7 +92,7 @@ contract SystemConfigInterop_Test is CommonTest { address(optimismPortal), abi.encodeCall( IOptimismPortalInterop.setConfig, - (ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)) + (Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)) ) ); @@ -117,12 +117,15 @@ contract SystemConfigInterop_Test is CommonTest { vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SYMBOL_SLOT, bytes32(0)); systemConfig.initialize({ - _owner: alice, + _roles: ISystemConfig.Roles({ + owner: alice, + feeAdmin: bob, + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), _basefeeScalar: 2100, _blobbasefeeScalar: 1000000, - _batcherHash: bytes32(hex"abcd"), _gasLimit: 30_000_000, - _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ diff --git a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol index bf63a700c098..520adc77c55b 100644 --- a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol @@ -8,7 +8,7 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract FeeVault_Test is Bridge_Initializer { +contract BaseFeeVault_Test is Bridge_Initializer { /// @dev Tests that the constructor sets the correct values. function test_constructor_baseFeeVault_succeeds() external view { assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol index a80f633fede2..c317519c5dbd 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol @@ -7,7 +7,6 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; import { Encoding } from "src/libraries/Encoding.sol"; -import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; // Target contract dependencies import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; @@ -39,18 +38,6 @@ contract CrossDomainOwnable2_Test is Bridge_Initializer { setter.set(1); } - /// @dev Tests that the `onlyOwner` modifier reverts when not called by the owner. - function test_onlyOwner_notOwner_reverts() external { - // set the xDomainMsgSender storage slot - bytes32 key = bytes32(uint256(204)); - bytes32 value = Bytes32AddressLib.fillLast12Bytes(address(alice)); - vm.store(address(l2CrossDomainMessenger), key, value); - - vm.prank(address(l2CrossDomainMessenger)); - vm.expectRevert("CrossDomainOwnable2: caller is not the owner"); - setter.set(1); - } - /// @dev Tests that the `onlyOwner` modifier causes the relayed message to fail. function test_onlyOwner_notOwner2_reverts() external { uint240 nonce = 0; diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol index 968910478198..6c86ccb50b11 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol @@ -71,7 +71,7 @@ contract CrossDomainOwnable3_Test is Bridge_Initializer { setter.transferOwnership({ _owner: alice, _isLocal: false }); // set the xDomainMsgSender storage slot - bytes32 key = bytes32(uint256(204)); + bytes32 key = bytes32(uint256(208)); bytes32 value = Bytes32AddressLib.fillLast12Bytes(bob); vm.store(address(l2CrossDomainMessenger), key, value); diff --git a/packages/contracts-bedrock/test/L2/FeeVault.t.sol b/packages/contracts-bedrock/test/L2/FeeVault.t.sol new file mode 100644 index 000000000000..bf63a700c098 --- /dev/null +++ b/packages/contracts-bedrock/test/L2/FeeVault.t.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing utilities +import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; + +// Libraries +import { Types } from "src/libraries/Types.sol"; + +// Test the implementations of the FeeVault +contract FeeVault_Test is Bridge_Initializer { + /// @dev Tests that the constructor sets the correct values. + function test_constructor_baseFeeVault_succeeds() external view { + assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); + assertEq(baseFeeVault.recipient(), deploy.cfg().baseFeeVaultRecipient()); + assertEq(baseFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); + assertEq(baseFeeVault.minWithdrawalAmount(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); + assertEq(uint8(baseFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L1)); + assertEq(uint8(baseFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); + } +} diff --git a/packages/contracts-bedrock/test/L2/L1Block.t.sol b/packages/contracts-bedrock/test/L2/L1Block.t.sol index 762553a2ff2f..58fc6e08ea4e 100644 --- a/packages/contracts-bedrock/test/L2/L1Block.t.sol +++ b/packages/contracts-bedrock/test/L2/L1Block.t.sol @@ -6,8 +6,11 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Encoding } from "src/libraries/Encoding.sol"; +import { Types } from "src/libraries/Types.sol"; +import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { Constants } from "src/libraries/Constants.sol"; import "src/libraries/L1BlockErrors.sol"; +import { LibString } from "lib/solady/src/utils/LibString.sol"; contract L1BlockTest is CommonTest { address depositor; @@ -165,6 +168,148 @@ contract L1BlockEcotone_Test is L1BlockTest { } } +contract L1BlockIsthmus_Test is L1BlockTest { + /// @dev Tests that `setIsthmus` succeeds if sender address is the depositor + function test_setIsthmus_isDepositor_succeeds() external { + vm.prank(depositor); + l1Block.setIsthmus(); + assertTrue(l1Block.isIsthmus()); + + bytes memory baseFeeVaultConfig = l1Block.getConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG); + (address recipient, uint256 min, Types.WithdrawalNetwork network) = + Encoding.decodeFeeVaultConfig(bytes32(baseFeeVaultConfig)); + assertEq(recipient, deploy.cfg().baseFeeVaultRecipient()); + assertEq(min, deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); + assertEq(uint8(network), uint8(deploy.cfg().baseFeeVaultWithdrawalNetwork())); + + bytes memory sequencerFeeVaultConfig = l1Block.getConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); + (recipient, min, network) = Encoding.decodeFeeVaultConfig(bytes32(sequencerFeeVaultConfig)); + assertEq(recipient, deploy.cfg().sequencerFeeVaultRecipient()); + assertEq(min, deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); + assertEq(uint8(network), uint8(deploy.cfg().sequencerFeeVaultWithdrawalNetwork())); + + bytes memory l1FeeVaultConfig = l1Block.getConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG); + (recipient, min, network) = Encoding.decodeFeeVaultConfig(bytes32(l1FeeVaultConfig)); + assertEq(recipient, deploy.cfg().l1FeeVaultRecipient()); + assertEq(min, deploy.cfg().l1FeeVaultMinimumWithdrawalAmount()); + assertEq(uint8(network), uint8(deploy.cfg().l1FeeVaultWithdrawalNetwork())); + + assertEq( + abi.decode(l1Block.getConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS), (address)), + address(l1ERC721Bridge) + ); + assertEq( + abi.decode(l1Block.getConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS), (address)), + address(l1CrossDomainMessenger) + ); + assertEq( + abi.decode(l1Block.getConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS), (address)), + address(l1StandardBridge) + ); + assertEq(abi.decode(l1Block.getConfig(Types.ConfigType.REMOTE_CHAIN_ID), (uint256)), deploy.cfg().l1ChainID()); + } + + /// @dev Tests that `setIsthmus` reverts if sender address is not the depositor + function test_setIsthmus_notDepositor_reverts() external { + vm.expectRevert(NotDepositor.selector); + l1Block.setIsthmus(); + } +} + +contract L1BlockConfig_Test is L1BlockTest { + /// @notice Ensures that `setConfig` always reverts when called, across all possible config types. + /// Use a magic number of 10 since solidity doesn't offer a good way to know the nubmer + /// of enum elements. + function test_setConfig_onlyDepositor_reverts(address _caller, uint8 _type) external { + vm.assume(_caller != Constants.DEPOSITOR_ACCOUNT); + vm.assume(_type < 10); // the number of defined config types + vm.expectRevert(NotDepositor.selector); + vm.prank(_caller); + l1Block.setConfig(Types.ConfigType(_type), hex""); + } + + function test_getConfigRoundtripGasPayingToken_succeeds( + address _token, + uint8 _decimals, + bytes32 _name, + bytes32 _symbol + ) + external + { + _name = LibString.normalizeSmallString(_name); + _symbol = LibString.normalizeSmallString(_symbol); + // Both the 0 address and the ether address are prevented from being set by + // `_setGasPayingToken` in `SystemConfig.sol` + vm.assume(_token != address(0) && _token != Constants.ETHER); + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, abi.encode(_token, _decimals, _name, _symbol)); + bytes memory data = l1Block.getConfig(Types.ConfigType.GAS_PAYING_TOKEN); + (address token, uint8 decimals, bytes32 name, bytes32 symbol) = + abi.decode(data, (address, uint8, bytes32, bytes32)); + assertEq(token, _token); + assertEq(decimals, _decimals); + + assertEq(name, _name); + assertEq(symbol, _symbol); + } + + /// @notice Tests roundtrip setConfig/getConfig for base fee vault config + function test_getConfigRoundtripBaseFeeVault_succeeds(bytes32 _config) external { + _getConfigRoundTrip(_config, Types.ConfigType.BASE_FEE_VAULT_CONFIG); + } + + /// @notice Tests roundtrip setConfig/getConfig for L1 fee vault config + function test_getConfigRoundtripL1FeeVault_succeeds(bytes32 _config) external { + _getConfigRoundTrip(_config, Types.ConfigType.L1_FEE_VAULT_CONFIG); + } + + /// @notice Tests roundtrip setConfig/getConfig for sequencer fee vault config + function test_getConfigRoundtripSequencerFeeVault_succeeds(bytes32 _config) external { + _getConfigRoundTrip(_config, Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); + } + + /// @notice Internal function for logic on round trip testing fee vault config + function _getConfigRoundTrip(bytes32 _config, Types.ConfigType _type) internal { + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(_type, abi.encode(_config)); + bytes memory data = l1Block.getConfig(_type); + bytes32 config = abi.decode(data, (bytes32)); + assertEq(config, _config); + } + + function test_getConfigRoundtripL1CrossDomainMessenger_succeeds(address _addr) external { + _getConfigRoundTrip(_addr, Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS); + } + + function test_getConfigRoundtripL1ERC721Bridge_succeeds(address _addr) external { + _getConfigRoundTrip(_addr, Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); + } + + function test_getConfigRoundtripL1StandardBridge_succeeds(address _addr) external { + _getConfigRoundTrip(_addr, Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); + } + + function _getConfigRoundTrip(address _addr, Types.ConfigType _type) internal { + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(_type, abi.encode(_addr)); + bytes memory data = l1Block.getConfig(_type); + address addr = abi.decode(data, (address)); + assertEq(addr, _addr); + } + + function test_getConfigRoundtripRemoteChainId_succeeds(uint256 _value) external { + _getConfigRoundTrip(_value, Types.ConfigType.REMOTE_CHAIN_ID); + } + + function _getConfigRoundTrip(uint256 _value, Types.ConfigType _type) internal { + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(_type, abi.encode(_value)); + bytes memory data = l1Block.getConfig(_type); + uint256 value = abi.decode(data, (uint256)); + assertEq(value, _value); + } +} + contract L1BlockCustomGasToken_Test is L1BlockTest { function testFuzz_setGasPayingToken_succeeds( address _token, diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol index 6f0ef2188b8c..fca80975141c 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol @@ -6,9 +6,10 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { StaticConfig } from "src/libraries/StaticConfig.sol"; +import { Types } from "src/libraries/Types.sol"; // Target contract dependencies -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; +import { L1BlockInterop } from "src/L2/L1BlockInterop.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import "src/libraries/L1BlockErrors.sol"; @@ -34,7 +35,7 @@ contract L1BlockInteropTest is CommonTest { function testFuzz_isInDependencySet_succeeds(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); assertTrue(_l1BlockInterop().isInDependencySet(_chainId)); } @@ -70,7 +71,7 @@ contract L1BlockInteropTest is CommonTest { for (uint256 i = 0; i < _dependencySetSize; i++) { if (i == block.chainid) continue; - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); uniqueCount++; } @@ -98,7 +99,7 @@ contract L1BlockInteropTest is CommonTest { emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); _l1BlockInterop().setConfig( - ConfigType.SET_GAS_PAYING_TOKEN, + Types.ConfigType.GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }) ); } @@ -116,7 +117,7 @@ contract L1BlockInteropTest is CommonTest { vm.expectRevert(NotDepositor.selector); _l1BlockInterop().setConfig( - ConfigType.SET_GAS_PAYING_TOKEN, + Types.ConfigType.GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }) ); } @@ -128,41 +129,41 @@ contract L1BlockInteropTest is CommonTest { vm.expectEmit(address(l1Block)); emit DependencyAdded(_chainId); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that adding a dependency reverts if it's the chain's chain id function test_setConfig_addDependency_chainChainId_reverts() public prankDepositor { vm.expectRevert(AlreadyDependency.selector); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(block.chainid)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(block.chainid)); } /// @dev Tests that adding a dependency already in the set reverts function test_setConfig_addDependency_alreadyDependency_reverts(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); vm.expectRevert(AlreadyDependency.selector); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that setting the add dependency config as not the depositor reverts. function testFuzz_setConfig_addDependency_notDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that setting the add dependency config when the dependency set size is too large reverts. function test_setConfig_addDependency_dependencySetSizeTooLarge_reverts() public prankDepositor { for (uint256 i = 0; i < type(uint8).max; i++) { - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); } assertEq(_l1BlockInterop().dependencySetSize(), type(uint8).max); vm.expectRevert(DependencySetSizeTooLarge.selector); - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(1)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(1)); } /// @dev Tests that the config for removing a dependency can be set. @@ -170,24 +171,26 @@ contract L1BlockInteropTest is CommonTest { vm.assume(_chainId != block.chainid); // Add the chain ID to the dependency set before removing it - _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); vm.expectEmit(address(l1Block)); emit DependencyRemoved(_chainId); - _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Tests that setting the remove dependency config as not the depositor reverts. function testFuzz_setConfig_removeDependency_notDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Tests that setting the remove dependency config for the chain's chain ID reverts. function test_setConfig_removeDependency_chainChainId_reverts() public prankDepositor { vm.expectRevert(CantRemovedDependency.selector); - _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(block.chainid)); + _l1BlockInterop().setConfig( + Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(block.chainid) + ); } /// @dev Tests that setting the remove dependency config for a chain ID that is not in the dependency set reverts. @@ -195,7 +198,7 @@ contract L1BlockInteropTest is CommonTest { vm.assume(_chainId != block.chainid); vm.expectRevert(NotDependency.selector); - _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Returns the L1BlockInterop instance. diff --git a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol index 03a3e7e5ad9b..825ee77e5c1f 100644 --- a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol @@ -8,7 +8,7 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract FeeVault_Test is Bridge_Initializer { +contract L1FeeVault_Test is Bridge_Initializer { /// @dev Tests that the constructor sets the correct values. function test_constructor_l1FeeVault_succeeds() external view { assertEq(l1FeeVault.RECIPIENT(), deploy.cfg().l1FeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index 1dc5749d6d54..bd6ee0cd28b6 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -21,12 +21,14 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { address recipient = address(0xabbaacdc); /// @dev Tests that the implementation is initialized correctly. + /// The implementation returns the actual address rather than `address(0)` + /// because the contract calls L1Block for the result. function test_constructor_succeeds() external view { IL2CrossDomainMessenger impl = IL2CrossDomainMessenger(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2CrossDomainMessenger"))); - assertEq(address(impl.OTHER_MESSENGER()), address(0)); - assertEq(address(impl.otherMessenger()), address(0)); - assertEq(address(impl.l1CrossDomainMessenger()), address(0)); + assertEq(address(impl.OTHER_MESSENGER()), address(l1CrossDomainMessenger)); + assertEq(address(impl.otherMessenger()), address(l1CrossDomainMessenger)); + assertEq(address(impl.l1CrossDomainMessenger()), address(l1CrossDomainMessenger)); } /// @dev Tests that the proxy is initialized correctly. diff --git a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol index 58179cb93207..54993d666a4f 100644 --- a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol @@ -22,7 +22,7 @@ contract TestERC721 is ERC721 { contract TestMintableERC721 is OptimismMintableERC721 { constructor( - address _bridge, + IL2ERC721Bridge _bridge, address _remoteToken ) OptimismMintableERC721(_bridge, 1, _remoteToken, "Test", "TST") @@ -61,7 +61,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { super.setUp(); remoteToken = new TestERC721(); - localToken = new TestMintableERC721(address(l2ERC721Bridge), address(remoteToken)); + localToken = new TestMintableERC721(l2ERC721Bridge, address(remoteToken)); // Mint alice a token. localToken.mint(alice, tokenId); diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 57b22e628eba..827849826836 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -191,7 +191,7 @@ contract L2GenesisTest is Test { // 16 prefunded dev accounts are excluded assertEq(expected, getJSONKeyCount(_path), "key count check"); - // 3 slots: implementation, owner, admin - assertEq(3, getStorageKeysCount(_path, Predeploys.PROXY_ADMIN), "proxy admin storage check"); + // 2 slots: implementation, admin + assertEq(2, getStorageKeysCount(_path, Predeploys.PROXY_ADMIN), "proxy admin storage check"); } } diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol index d703ec95f64e..f4fadb06dd31 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol @@ -28,12 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer { function test_constructor_succeeds() external view { IL2StandardBridge impl = IL2StandardBridge(payable(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2StandardBridge")))); - // The implementation contract is initialized with a 0 L1 bridge address, + // The implementation contract calls out to L1Block to get the otherBridge // but the L2 cross-domain-messenger is always set to the predeploy address for both proxy and implementation. - assertEq(address(impl.MESSENGER()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check MESSENGER"); - assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check messenger"); - assertEq(address(impl.OTHER_BRIDGE()), address(0), "constructor zero check OTHER_BRIDGE"); - assertEq(address(impl.otherBridge()), address(0), "constructor zero check otherBridge"); + assertEq(address(impl.MESSENGER()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor check MESSENGER"); + assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor check messenger"); + assertEq(address(impl.OTHER_BRIDGE()), address(l1StandardBridge), "constructor check OTHER_BRIDGE"); + assertEq(address(impl.otherBridge()), address(l1StandardBridge), "constructor check otherBridge"); } /// @dev Tests that the bridge is initialized correctly. diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol b/packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol similarity index 89% rename from packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol rename to packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol index ef9019eafa04..f5256cb73cbb 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol @@ -4,16 +4,15 @@ pragma solidity 0.8.15; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; -import { OptimismMintableERC721Factory } from "src/universal/OptimismMintableERC721Factory.sol"; contract OptimismMintableERC721Factory_Test is Bridge_Initializer { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); function test_constructor_succeeds() external view { - assertEq(l2OptimismMintableERC721Factory.BRIDGE(), address(l2ERC721Bridge)); - assertEq(l2OptimismMintableERC721Factory.bridge(), address(l2ERC721Bridge)); + assertEq(address(l2OptimismMintableERC721Factory.BRIDGE()), address(l2ERC721Bridge)); + assertEq(address(l2OptimismMintableERC721Factory.bridge()), address(l2ERC721Bridge)); assertEq(l2OptimismMintableERC721Factory.REMOTE_CHAIN_ID(), deploy.cfg().l1ChainID()); - assertEq(l2OptimismMintableERC721Factory.remoteChainID(), deploy.cfg().l1ChainID()); + assertEq(l2OptimismMintableERC721Factory.remoteChainId(), deploy.cfg().l1ChainID()); } function test_createOptimismMintableERC721_succeeds() external { @@ -40,7 +39,7 @@ contract OptimismMintableERC721Factory_Test is Bridge_Initializer { assertEq(created.name(), "L2Token"); assertEq(created.symbol(), "L2T"); assertEq(created.REMOTE_TOKEN(), remote); - assertEq(created.BRIDGE(), address(l2ERC721Bridge)); + assertEq(address(created.BRIDGE()), address(l2ERC721Bridge)); assertEq(created.REMOTE_CHAIN_ID(), deploy.cfg().l1ChainID()); } diff --git a/packages/contracts-bedrock/test/L2/Predeploys.t.sol b/packages/contracts-bedrock/test/L2/Predeploys.t.sol index 27e3d75b2fdc..38e8ff74be69 100644 --- a/packages/contracts-bedrock/test/L2/Predeploys.t.sol +++ b/packages/contracts-bedrock/test/L2/Predeploys.t.sol @@ -21,10 +21,9 @@ contract PredeploysBaseTest is CommonTest { return _addr == Predeploys.L1_MESSAGE_SENDER; } - /// @dev Returns true if the predeploy is initializable. - function _isInitializable(address _addr) internal pure returns (bool) { - return _addr == Predeploys.L2_CROSS_DOMAIN_MESSENGER || _addr == Predeploys.L2_STANDARD_BRIDGE - || _addr == Predeploys.L2_ERC721_BRIDGE || _addr == Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY; + /// @dev No predeploys should ever be initializable. + function _isInitializable(address) internal pure returns (bool) { + return false; } /// @dev Returns true if the predeploy uses immutables. @@ -49,7 +48,7 @@ contract PredeploysBaseTest is CommonTest { ); } - function _test_predeploys(bool _useInterop) internal { + function _test_predeploys(bool _useInterop) internal view { uint256 count = 2048; uint160 prefix = uint160(0x420) << 148; @@ -104,10 +103,6 @@ contract PredeploysBaseTest is CommonTest { // can't check bytecode if it's modified with immutables in genesis. assertEq(implAddr.code, supposedCode, "proxy implementation contract should match contract source"); } - - if (_isInitializable(addr)) { - assertEq(l2Genesis.loadInitializedSlot(cname), uint8(1)); - } } } } @@ -115,7 +110,7 @@ contract PredeploysBaseTest is CommonTest { contract PredeploysTest is PredeploysBaseTest { /// @dev Tests that the predeploy addresses are set correctly. They have code /// and the proxied accounts have the correct admin. - function test_predeploys_succeeds() external { + function test_predeploys_succeeds() external view { _test_predeploys(false); } } @@ -129,7 +124,7 @@ contract PredeploysInteropTest is PredeploysBaseTest { /// @dev Tests that the predeploy addresses are set correctly. They have code /// and the proxied accounts have the correct admin. Using interop. - function test_predeploys_succeeds() external { + function test_predeploys_succeeds() external view { _test_predeploys(true); } } diff --git a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol index ca8c3806b38b..9ba232dcf0b3 100644 --- a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol @@ -4,33 +4,29 @@ pragma solidity 0.8.15; // Testing import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; -import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; - -// Contracts -import { ISequencerFeeVault } from "src/L2/interfaces/ISequencerFeeVault.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; import { Types } from "src/libraries/Types.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; +import { Constants } from "src/libraries/Constants.sol"; contract SequencerFeeVault_Test is CommonTest { - address recipient; - - /// @dev Sets up the test suite. function setUp() public override { super.setUp(); - recipient = deploy.cfg().sequencerFeeVaultRecipient(); + assertEq(uint8(deploy.cfg().sequencerFeeVaultWithdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); } - /// @dev Tests that the l1 fee wallet is correct. + /// @dev Tests that the sequencer fee wallet is correct. function test_constructor_succeeds() external view { + address recipient = deploy.cfg().sequencerFeeVaultRecipient(); + uint256 amount = deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(); assertEq(sequencerFeeVault.l1FeeWallet(), recipient); assertEq(sequencerFeeVault.RECIPIENT(), recipient); assertEq(sequencerFeeVault.recipient(), recipient); - assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); - assertEq(sequencerFeeVault.minWithdrawalAmount(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); + assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), amount); + assertEq(sequencerFeeVault.minWithdrawalAmount(), amount); assertEq(uint8(sequencerFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L1)); assertEq(uint8(sequencerFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); } @@ -63,6 +59,8 @@ contract SequencerFeeVault_Test is CommonTest { // No ether has been withdrawn yet assertEq(sequencerFeeVault.totalProcessed(), 0); + address recipient = deploy.cfg().sequencerFeeVaultRecipient(); + vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); emit Withdrawal(address(sequencerFeeVault).balance, recipient, address(this)); vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); @@ -102,34 +100,31 @@ contract SequencerFeeVault_Test is CommonTest { } contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { - /// @dev a cache for the config fee recipient - address recipient; - /// @dev Sets up the test suite. function setUp() public override { super.setUp(); - // Alter the deployment to use WithdrawalNetwork.L2 - vm.etch( - EIP1967Helper.getImplementation(Predeploys.SEQUENCER_FEE_WALLET), - address( - DeployUtils.create1({ - _name: "SequencerFeeVault", - _args: DeployUtils.encodeConstructor( - abi.encodeCall( - ISequencerFeeVault.__constructor__, - ( - deploy.cfg().sequencerFeeVaultRecipient(), - deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), - Types.WithdrawalNetwork.L2 - ) - ) - ) - }) - ).code - ); + // Explicitly use L2 withdrawal network + bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: deploy.cfg().sequencerFeeVaultRecipient(), + _amount: deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork.L2 + }); + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig)); + } - recipient = deploy.cfg().sequencerFeeVaultRecipient(); + /// @dev Tests that the sequencer fee wallet is correct. + function test_constructor_succeeds() external view { + address recipient = deploy.cfg().sequencerFeeVaultRecipient(); + uint256 amount = deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(); + assertEq(sequencerFeeVault.l1FeeWallet(), recipient); + assertEq(sequencerFeeVault.RECIPIENT(), recipient); + assertEq(sequencerFeeVault.recipient(), recipient); + assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), amount); + assertEq(sequencerFeeVault.minWithdrawalAmount(), amount); + assertEq(uint8(sequencerFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L2)); + assertEq(uint8(sequencerFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L2)); } /// @dev Tests that `withdraw` successfully initiates a withdrawal to L2. @@ -148,14 +143,14 @@ contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { ); // The entire vault's balance is withdrawn - vm.expectCall(recipient, address(sequencerFeeVault).balance, bytes("")); + vm.expectCall(sequencerFeeVault.RECIPIENT(), address(sequencerFeeVault).balance, bytes("")); sequencerFeeVault.withdraw(); // The withdrawal was successful assertEq(sequencerFeeVault.totalProcessed(), amount); assertEq(address(sequencerFeeVault).balance, 0); - assertEq(recipient.balance, amount); + assertEq(sequencerFeeVault.recipient().balance, amount); } /// @dev Tests that `withdraw` fails if the Recipient reverts. This also serves to simulate @@ -171,7 +166,7 @@ contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { vm.etch(sequencerFeeVault.RECIPIENT(), type(Reverter).runtimeCode); // The entire vault's balance is withdrawn - vm.expectCall(recipient, address(sequencerFeeVault).balance, bytes("")); + vm.expectCall(sequencerFeeVault.recipient(), address(sequencerFeeVault).balance, bytes("")); vm.expectRevert("FeeVault: failed to send ETH to L2 fee recipient"); sequencerFeeVault.withdraw(); assertEq(sequencerFeeVault.totalProcessed(), 0); diff --git a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol index 5b0b300abda0..31700f3a7529 100644 --- a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol @@ -7,6 +7,12 @@ import { IProxy } from "src/universal/interfaces/IProxy.sol"; import { Constants } from "src/libraries/Constants.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +contract ConfigSetter { + function setConfig(uint8, bytes calldata) external { + // noop + } +} + contract SystemConfig_GasLimitBoundaries_Invariant is Test { ISystemConfig public config; @@ -23,6 +29,7 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) }) ); + ConfigSetter setter = new ConfigSetter(); vm.prank(msg.sender); proxy.upgradeToAndCall( @@ -30,12 +37,15 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { abi.encodeCall( configImpl.initialize, ( - address(0xbeef), // owner + ISystemConfig.Roles({ + owner: address(0xbeef), + feeAdmin: address(0xbeef), + unsafeBlockSigner: address(1), + batcherHash: bytes32(hex"abcd") + }), 2100, // overhead 1000000, // scalar - bytes32(hex"abcd"), // batcher hash 30_000_000, // gas limit - address(1), // unsafe block signer Constants.DEFAULT_RESOURCE_CONFIG(), address(0), // _batchInbox ISystemConfig.Addresses({ // _addrs @@ -43,7 +53,7 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(0), + optimismPortal: address(setter), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) diff --git a/packages/contracts-bedrock/test/libraries/Encoding.t.sol b/packages/contracts-bedrock/test/libraries/Encoding.t.sol index a301fdd97b3a..11df7eeae17e 100644 --- a/packages/contracts-bedrock/test/libraries/Encoding.t.sol +++ b/packages/contracts-bedrock/test/libraries/Encoding.t.sol @@ -93,4 +93,15 @@ contract Encoding_Test is CommonTest { assertEq(txn, _txn); } + + // using a bool simulates the 2 enum values that exist + function testFuzz_encodeFeeVaultConfig_succeeds(address _recipient, uint88 _amount, bool _network) public pure { + Types.WithdrawalNetwork _withdrawalNetwork = _network ? Types.WithdrawalNetwork.L1 : Types.WithdrawalNetwork.L2; + bytes32 encoded = Encoding.encodeFeeVaultConfig(_recipient, uint256(_amount), _withdrawalNetwork); + (address recipient, uint256 amount, Types.WithdrawalNetwork withdrawalNetwork) = + Encoding.decodeFeeVaultConfig(encoded); + assertEq(_recipient, recipient, "bad recipient"); + assertEq(uint256(_amount), amount, "bad amount"); + assertEq(uint256(withdrawalNetwork), uint256(withdrawalNetwork), "bad network"); + } } diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 54e0e456def3..6f1f59c9fe96 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -35,6 +35,7 @@ contract DeployOPChainInput_Test is Test { // Define defaults. address opChainProxyAdminOwner = makeAddr("opChainProxyAdminOwner"); address systemConfigOwner = makeAddr("systemConfigOwner"); + address systemConfigFeeAdmin = makeAddr("systemConfigFeeAdmin"); address batcher = makeAddr("batcher"); address unsafeBlockSigner = makeAddr("unsafeBlockSigner"); address proposer = makeAddr("proposer"); @@ -52,6 +53,7 @@ contract DeployOPChainInput_Test is Test { function test_set_succeeds() public { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); + doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); @@ -67,6 +69,7 @@ contract DeployOPChainInput_Test is Test { // Compare the default inputs to the getter methods. assertEq(opChainProxyAdminOwner, doi.opChainProxyAdminOwner(), "200"); assertEq(systemConfigOwner, doi.systemConfigOwner(), "300"); + assertEq(systemConfigFeeAdmin, doi.systemConfigFeeAdmin(), "310"); assertEq(batcher, doi.batcher(), "400"); assertEq(unsafeBlockSigner, doi.unsafeBlockSigner(), "500"); assertEq(proposer, doi.proposer(), "600"); @@ -87,6 +90,9 @@ contract DeployOPChainInput_Test is Test { vm.expectRevert(expectedErr); doi.systemConfigOwner(); + vm.expectRevert(expectedErr); + doi.systemConfigFeeAdmin(); + vm.expectRevert(expectedErr); doi.batcher(); @@ -330,6 +336,7 @@ contract DeployOPChain_TestBase is Test { // `opcm` is set during `setUp` since it is an output of the previous step. address opChainProxyAdminOwner = makeAddr("defaultOPChainProxyAdminOwner"); address systemConfigOwner = makeAddr("defaultSystemConfigOwner"); + address systemConfigFeeAdmin = makeAddr("defaultSystemConfigFeeAdmin"); address batcher = makeAddr("defaultBatcher"); address unsafeBlockSigner = makeAddr("defaultUnsafeBlockSigner"); address proposer = makeAddr("defaultProposer"); @@ -430,13 +437,14 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { function testFuzz_run_memory_succeed(bytes32 _seed) public { opChainProxyAdminOwner = address(uint160(uint256(hash(_seed, 0)))); systemConfigOwner = address(uint160(uint256(hash(_seed, 1)))); - batcher = address(uint160(uint256(hash(_seed, 2)))); - unsafeBlockSigner = address(uint160(uint256(hash(_seed, 3)))); - proposer = address(uint160(uint256(hash(_seed, 4)))); - challenger = address(uint160(uint256(hash(_seed, 5)))); - basefeeScalar = uint32(uint256(hash(_seed, 6))); - blobBaseFeeScalar = uint32(uint256(hash(_seed, 7))); - l2ChainId = uint256(hash(_seed, 8)); + systemConfigFeeAdmin = address(uint160(uint256(hash(_seed, 2)))); + batcher = address(uint160(uint256(hash(_seed, 3)))); + unsafeBlockSigner = address(uint160(uint256(hash(_seed, 4)))); + proposer = address(uint160(uint256(hash(_seed, 5)))); + challenger = address(uint160(uint256(hash(_seed, 6)))); + basefeeScalar = uint32(uint256(hash(_seed, 7))); + blobBaseFeeScalar = uint32(uint256(hash(_seed, 8))); + l2ChainId = uint256(hash(_seed, 9)); // Set the initial anchor states. The typical usage we expect is to pass in one root per game type. uint256 cannonBlock = uint256(hash(_seed, 9)); @@ -459,6 +467,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); + doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); @@ -483,6 +492,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { // Assert that individual input fields were properly set based on the inputs. assertEq(opChainProxyAdminOwner, doi.opChainProxyAdminOwner(), "100"); assertEq(systemConfigOwner, doi.systemConfigOwner(), "200"); + assertEq(systemConfigFeeAdmin, doi.systemConfigFeeAdmin(), "210"); assertEq(batcher, doi.batcher(), "300"); assertEq(unsafeBlockSigner, doi.unsafeBlockSigner(), "400"); assertEq(proposer, doi.proposer(), "500"); @@ -502,6 +512,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { // Assert inputs were properly passed through to the contract initializers. assertEq(address(doo.opChainProxyAdmin().owner()), opChainProxyAdminOwner, "2100"); assertEq(address(doo.systemConfigProxy().owner()), systemConfigOwner, "2200"); + assertEq(address(doo.systemConfigProxy().feeAdmin()), systemConfigFeeAdmin, "2210"); address batcherActual = address(uint160(uint256(doo.systemConfigProxy().batcherHash()))); assertEq(batcherActual, batcher, "2300"); assertEq(address(doo.systemConfigProxy().unsafeBlockSigner()), unsafeBlockSigner, "2400"); @@ -552,6 +563,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { function setDOI() internal { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); + doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index ef2b654b2410..e324aec5f3b5 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -15,6 +15,9 @@ import { OutputMode, Fork, ForkUtils } from "scripts/libraries/Config.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; +import { Constants } from "src/libraries/Constants.sol"; +import { Encoding } from "src/libraries/Encoding.sol"; +import { Types } from "src/libraries/Types.sol"; // Interfaces import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; @@ -27,7 +30,7 @@ import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityC import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; -import { IOptimismMintableERC721Factory } from "src/universal/interfaces/IOptimismMintableERC721Factory.sol"; +import { IOptimismMintableERC721Factory } from "src/L2/interfaces/IOptimismMintableERC721Factory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; @@ -35,7 +38,8 @@ import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMesseng import { IL2StandardBridgeInterop } from "src/L2/interfaces/IL2StandardBridgeInterop.sol"; import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; -import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; +import { IL1OptimismMintableERC20Factory } from "src/L1/interfaces/IL1OptimismMintableERC20Factory.sol"; +import { IL2OptimismMintableERC20Factory } from "src/L2/interfaces/IL2OptimismMintableERC20Factory.sol"; import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol"; import { IBaseFeeVault } from "src/L2/interfaces/IBaseFeeVault.sol"; @@ -83,7 +87,7 @@ contract Setup { IL1CrossDomainMessenger l1CrossDomainMessenger; IAddressManager addressManager; IL1ERC721Bridge l1ERC721Bridge; - IOptimismMintableERC20Factory l1OptimismMintableERC20Factory; + IL1OptimismMintableERC20Factory l1OptimismMintableERC20Factory; IProtocolVersions protocolVersions; ISuperchainConfig superchainConfig; IDataAvailabilityChallenge dataAvailabilityChallenge; @@ -93,8 +97,8 @@ contract Setup { IL2CrossDomainMessenger(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER)); IL2StandardBridgeInterop l2StandardBridge = IL2StandardBridgeInterop(payable(Predeploys.L2_STANDARD_BRIDGE)); IL2ToL1MessagePasser l2ToL1MessagePasser = IL2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)); - IOptimismMintableERC20Factory l2OptimismMintableERC20Factory = - IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); + IL2OptimismMintableERC20Factory l2OptimismMintableERC20Factory = + IL2OptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); IL2ERC721Bridge l2ERC721Bridge = IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE); IOptimismMintableERC721Factory l2OptimismMintableERC721Factory = IOptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); @@ -154,7 +158,7 @@ contract Setup { addressManager = IAddressManager(deploy.mustGetAddress("AddressManager")); l1ERC721Bridge = IL1ERC721Bridge(deploy.mustGetAddress("L1ERC721BridgeProxy")); l1OptimismMintableERC20Factory = - IOptimismMintableERC20Factory(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy")); + IL1OptimismMintableERC20Factory(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy")); protocolVersions = IProtocolVersions(deploy.mustGetAddress("ProtocolVersionsProxy")); superchainConfig = ISuperchainConfig(deploy.mustGetAddress("SuperchainConfigProxy")); anchorStateRegistry = IAnchorStateRegistry(deploy.mustGetAddress("AnchorStateRegistryProxy")); @@ -174,8 +178,8 @@ contract Setup { vm.label(address(addressManager), "AddressManager"); vm.label(address(l1ERC721Bridge), "L1ERC721Bridge"); vm.label(deploy.mustGetAddress("L1ERC721BridgeProxy"), "L1ERC721BridgeProxy"); - vm.label(address(l1OptimismMintableERC20Factory), "OptimismMintableERC20Factory"); - vm.label(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy"), "OptimismMintableERC20FactoryProxy"); + vm.label(address(l1OptimismMintableERC20Factory), "L1OptimismMintableERC20Factory"); + vm.label(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy"), "L1OptimismMintableERC20FactoryProxy"); vm.label(address(protocolVersions), "ProtocolVersions"); vm.label(deploy.mustGetAddress("ProtocolVersionsProxy"), "ProtocolVersionsProxy"); vm.label(address(superchainConfig), "SuperchainConfig"); @@ -200,15 +204,16 @@ contract Setup { /// @dev Sets up the L2 contracts. Depends on `L1()` being called first. function L2() public { console.log("Setup: creating L2 genesis with fork %s", l2Fork.toString()); - l2Genesis.runWithOptions( - OutputMode.NONE, - l2Fork, - L1Dependencies({ + l2Genesis.runWithOptions({ + _mode: OutputMode.NONE, + _fork: l2Fork, + _populateNetworkConfig: false, + _l1Dependencies: L1Dependencies({ l1CrossDomainMessengerProxy: payable(address(l1CrossDomainMessenger)), l1StandardBridgeProxy: payable(address(l1StandardBridge)), l1ERC721BridgeProxy: payable(address(l1ERC721Bridge)) }) - ); + }); // Set the governance token's owner to be the final system owner address finalSystemOwner = deploy.cfg().finalSystemOwner(); @@ -216,8 +221,46 @@ contract Setup { governanceToken.transferOwnership(finalSystemOwner); vm.stopPrank(); + // These calls by the depositor account simulate the SystemConfig setting the + // network specific configuration into L2. Ideally there is a library that automatically + // translates TransactionDeposited and ConfigUpdate events into the appropriate calls + vm.startPrank(Constants.DEPOSITOR_ACCOUNT); + l1Block.setConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS, abi.encode(l1ERC721Bridge)); + l1Block.setConfig(Types.ConfigType.REMOTE_CHAIN_ID, abi.encode(deploy.cfg().l1ChainID())); + l1Block.setConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS, abi.encode(l1CrossDomainMessenger)); + l1Block.setConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS, abi.encode(l1StandardBridge)); + + bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: deploy.cfg().sequencerFeeVaultRecipient(), + _amount: deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(deploy.cfg().sequencerFeeVaultWithdrawalNetwork()) + }); + l1Block.setConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig)); + + bytes32 baseFeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: deploy.cfg().baseFeeVaultRecipient(), + _amount: deploy.cfg().baseFeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(deploy.cfg().baseFeeVaultWithdrawalNetwork()) + }); + l1Block.setConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG, abi.encode(baseFeeVaultConfig)); + + bytes32 l1FeeVaultConfig = Encoding.encodeFeeVaultConfig({ + _recipient: deploy.cfg().l1FeeVaultRecipient(), + _amount: deploy.cfg().l1FeeVaultMinimumWithdrawalAmount(), + _network: Types.WithdrawalNetwork(deploy.cfg().l1FeeVaultWithdrawalNetwork()) + }); + l1Block.setConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG, abi.encode(l1FeeVaultConfig)); + vm.stopPrank(); + + // Reset the ResourceConfig gas used to 0 + bytes32 slot = vm.load(address(optimismPortal), bytes32(uint256(1))); + slot = bytes32(uint256(slot) & ~(uint256(type(uint64).max) << 128)); + vm.store(address(optimismPortal), bytes32(uint256(1)), slot); + vm.store(address(optimismPortal2), bytes32(uint256(1)), slot); + // L2 predeploys labelPredeploy(Predeploys.L2_STANDARD_BRIDGE); + labelPredeploy(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); labelPredeploy(Predeploys.L2_CROSS_DOMAIN_MESSENGER); labelPredeploy(Predeploys.L2_TO_L1_MESSAGE_PASSER); labelPredeploy(Predeploys.SEQUENCER_FEE_WALLET); @@ -232,6 +275,7 @@ contract Setup { labelPredeploy(Predeploys.EAS); labelPredeploy(Predeploys.SCHEMA_REGISTRY); labelPredeploy(Predeploys.WETH); + labelPredeploy(Predeploys.L2_ERC721_BRIDGE); labelPredeploy(Predeploys.SUPERCHAIN_WETH); labelPredeploy(Predeploys.ETH_LIQUIDITY); labelPredeploy(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY); @@ -259,7 +303,11 @@ contract Setup { } function labelPredeploy(address _addr) internal { - vm.label(_addr, Predeploys.getName(_addr)); + string memory name = Predeploys.getName(_addr); + vm.label(_addr, name); + if (!Predeploys.notProxied(_addr)) { + vm.label(Predeploys.predeployToCodeNamespace(_addr), string.concat(name, "Implementation")); + } } function labelPreinstall(address _addr) internal { diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol index d146b050f387..6b382eab117f 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol @@ -12,19 +12,11 @@ import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC2 // Interfaces import { IProxy } from "src/universal/interfaces/IProxy.sol"; -import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; contract OptimismMintableTokenFactory_Test is Bridge_Initializer { event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); - /// @notice Tests that the constructor is initialized correctly. - function test_constructor_succeeds() external { - IOptimismMintableERC20Factory impl = IOptimismMintableERC20Factory(address(new OptimismMintableERC20Factory())); - assertEq(address(impl.BRIDGE()), address(0)); - assertEq(address(impl.bridge()), address(0)); - } - /// @notice Tests that the proxy is initialized correctly. function test_initialize_succeeds() external view { assertEq(address(l1OptimismMintableERC20Factory.BRIDGE()), address(l1StandardBridge)); @@ -33,7 +25,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer { /// @notice Tests that the upgrade is successful. function test_upgrading_succeeds() external { - IProxy proxy = IProxy(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy")); + IProxy proxy = IProxy(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy")); // Check an unused slot before upgrading. bytes32 slot21Before = vm.load(address(l1OptimismMintableERC20Factory), bytes32(uint256(21))); assertEq(bytes32(0), slot21Before); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol index daea00064cf4..989699987219 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol @@ -23,7 +23,7 @@ contract OptimismMintableERC721_Test is Bridge_Initializer { // Set up the token pair. L1NFT = new ERC721("L1NFT", "L1T"); - L2NFT = new OptimismMintableERC721(address(l2ERC721Bridge), 1, address(L1NFT), "L2NFT", "L2T"); + L2NFT = new OptimismMintableERC721(l2ERC721Bridge, 1, address(L1NFT), "L2NFT", "L2T"); // Label the addresses for nice traces. vm.label(address(L1NFT), "L1ERC721Token"); @@ -34,10 +34,10 @@ contract OptimismMintableERC721_Test is Bridge_Initializer { assertEq(L2NFT.name(), "L2NFT"); assertEq(L2NFT.symbol(), "L2T"); assertEq(L2NFT.remoteToken(), address(L1NFT)); - assertEq(L2NFT.bridge(), address(l2ERC721Bridge)); + assertEq(address(L2NFT.bridge()), address(l2ERC721Bridge)); assertEq(L2NFT.remoteChainId(), 1); assertEq(L2NFT.REMOTE_TOKEN(), address(L1NFT)); - assertEq(L2NFT.BRIDGE(), address(l2ERC721Bridge)); + assertEq(address(L2NFT.BRIDGE()), address(l2ERC721Bridge)); assertEq(L2NFT.REMOTE_CHAIN_ID(), 1); } diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 9c22e178a5f4..ea53608a7a8e 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -14,7 +14,6 @@ import { OPContractsManager } from "src/L1/OPContractsManager.sol"; // Interfaces import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; @@ -42,7 +41,8 @@ contract Specification_Test is CommonTest { DELAYEDWETHOWNER, COUNCILSAFE, COUNCILSAFEOWNER, - DEPENDENCYMANAGER + DEPENDENCYMANAGER, + FEEADMIN } /// @notice Represents the specification of a function. @@ -276,7 +276,7 @@ contract Specification_Test is CommonTest { _name: "OptimismPortal", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortal", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); + _addSpec({ _name: "OptimismPortal", _sel: _getSel("setConfig(uint8,bytes)") }); // OptimismPortalInterop _addSpec({ @@ -335,12 +335,8 @@ contract Specification_Test is CommonTest { _name: "OptimismPortalInterop", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); - _addSpec({ - _name: "OptimismPortalInterop", - _sel: IOptimismPortalInterop.setConfig.selector, - _auth: Role.SYSTEMCONFIGOWNER - }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("upgrade(uint32,bytes)"), _auth: Role.FEEADMIN }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("setConfig(uint8,bytes)") }); // OptimismPortal2 _addSpec({ _name: "OptimismPortal2", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); @@ -387,7 +383,27 @@ contract Specification_Test is CommonTest { _name: "OptimismPortal2", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(uint32,bytes)"), _auth: Role.FEEADMIN }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("setConfig(uint8,bytes)") }); + + // L1OptimismMintableERC20Factory + _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("BRIDGE()") }); + _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("bridge()") }); + _addSpec({ + _name: "L1OptimismMintableERC20Factory", + _sel: _getSel("createOptimismMintableERC20(address,string,string)") + }); + _addSpec({ + _name: "L1OptimismMintableERC20Factory", + _sel: _getSel("createOptimismMintableERC20WithDecimals(address,string,string,uint8)") + }); + _addSpec({ + _name: "L1OptimismMintableERC20Factory", + _sel: _getSel("createStandardL2Token(address,string,string)") + }); + _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("deployments(address)") }); + _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("version()") }); + _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("initialize(address)") }); // ProtocolVersions _addSpec({ _name: "ProtocolVersions", _sel: _getSel("RECOMMENDED_SLOT()") }); @@ -418,11 +434,13 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SuperchainConfig", _sel: _getSel("GUARDIAN_SLOT()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("PAUSED_SLOT()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("guardian()") }); - _addSpec({ _name: "SuperchainConfig", _sel: _getSel("initialize(address,bool)") }); + _addSpec({ _name: "SuperchainConfig", _sel: _getSel("initialize(address,address,bool)") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("pause(string)"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("paused()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("unpause()"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("version()") }); + _addSpec({ _name: "SuperchainConfig", _sel: _getSel("UPGRADER_SLOT()") }); + _addSpec({ _name: "SuperchainConfig", _sel: _getSel("upgrader()") }); // SystemConfig _addSpec({ _name: "SystemConfig", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); @@ -436,6 +454,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: ISystemConfig.minimumGasLimit.selector }); _addSpec({ _name: "SystemConfig", _sel: _getSel("overhead()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("owner()") }); + _addSpec({ _name: "SystemConfig", _sel: _getSel("feeAdmin()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfig", _sel: ISystemConfig.resourceConfig.selector }); _addSpec({ _name: "SystemConfig", _sel: _getSel("scalar()") }); @@ -478,6 +497,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: _getSel("basefeeScalar()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("blobbasefeeScalar()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("maximumGasLimit()") }); + _addSpec({ _name: "SystemConfig", _sel: _getSel("setFeeVaultConfig(uint8,address,uint256,uint8)") }); // SystemConfigInterop _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); @@ -489,8 +509,15 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("eip1559Elasticity()") }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.initialize.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.minimumGasLimit.selector }); + _addSpec({ + _name: "SystemConfigInterop", + _sel: _getSel( + "initialize((address,address,address,bytes32),uint32,uint32,uint64,(uint32,uint8,uint8,uint32,uint32,uint128),address,(address,address,address,address,address,address,address),address)" + ) + }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("overhead()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("owner()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("feeAdmin()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.resourceConfig.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("scalar()") }); @@ -560,12 +587,7 @@ contract Specification_Test is CommonTest { _auth: Role.DEPENDENCYMANAGER }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("dependencyManager()") }); - _addSpec({ - _name: "SystemConfigInterop", - _sel: _getSel( - "initialize(address,uint32,uint32,bytes32,uint64,address,(uint32,uint8,uint8,uint32,uint32,uint128),address,(address,address,address,address,address,address,address),address)" - ) - }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("setFeeVaultConfig(uint8,address,uint256,uint8)") }); // ProxyAdmin _addSpec({ _name: "ProxyAdmin", _sel: _getSel("addressManager()") }); diff --git a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol index be7f8a51107c..22146147f129 100644 --- a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol @@ -2,10 +2,12 @@ pragma solidity 0.8.15; import { StandardBridge } from "src/universal/StandardBridge.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { OptimismMintableERC20, ILegacyMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { Constants } from "src/libraries/Constants.sol"; +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; /// @title StandardBridgeTester /// @notice Simple wrapper around the StandardBridge contract that exposes @@ -21,6 +23,14 @@ contract StandardBridgeTester is StandardBridge { return _isCorrectTokenPair(_mintableToken, _otherToken); } + function otherBridge() public pure override returns (IStandardBridge) { + return IStandardBridge(payable(address(0))); + } + + function messenger() public pure override returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(address(0)); + } + function gasPayingToken() internal pure override returns (address, uint8) { return (Constants.ETHER, 18); } diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 5c17987c471d..331b141bd124 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -28,6 +28,10 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr /// deepest contract in the inheritance chain for setting up the system contracts. /// For each L1 contract both the implementation and the proxy are tested. contract Initializer_Test is Bridge_Initializer { + /// @notice Error used by openzeppelin v5 initializable. + /// The contract is already initialized. + error InvalidInitialization(); + /// @notice Contains the address of an `Initializable` contract and the calldata /// used to initialize it. struct InitializeableContract { @@ -57,7 +61,7 @@ contract Initializer_Test is Bridge_Initializer { InitializeableContract({ name: "SuperchainConfig", target: deploy.mustGetAddress("SuperchainConfig"), - initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), false)) + initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), address(0), false)) }) ); // SuperchainConfigProxy @@ -65,7 +69,7 @@ contract Initializer_Test is Bridge_Initializer { InitializeableContract({ name: "SuperchainConfigProxy", target: address(superchainConfig), - initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), false)) + initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), address(0), false)) }) ); // L1CrossDomainMessengerImpl @@ -176,12 +180,15 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall( systemConfig.initialize, ( - address(0xdead), + ISystemConfig.Roles({ + owner: address(0xdead), + feeAdmin: address(0xdead), + unsafeBlockSigner: address(0), + batcherHash: bytes32(0) + }), 0, 0, - bytes32(0), 1, - address(0), IResourceMetering.ResourceConfig({ maxResourceLimit: 1, elasticityMultiplier: 1, @@ -212,12 +219,15 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall( systemConfig.initialize, ( - address(0xdead), + ISystemConfig.Roles({ + owner: address(0xdead), + feeAdmin: address(0xdead), + unsafeBlockSigner: address(0), + batcherHash: bytes32(0) + }), 0, 0, - bytes32(0), 1, - address(0), IResourceMetering.ResourceConfig({ maxResourceLimit: 1, elasticityMultiplier: 1, @@ -260,14 +270,6 @@ contract Initializer_Test is Bridge_Initializer { ) }) ); - // L2CrossDomainMessenger - contracts.push( - InitializeableContract({ - name: "L2CrossDomainMessenger", - target: address(l2CrossDomainMessenger), - initCalldata: abi.encodeCall(l2CrossDomainMessenger.initialize, (l1CrossDomainMessenger)) - }) - ); // L1StandardBridgeImpl contracts.push( InitializeableContract({ @@ -288,22 +290,6 @@ contract Initializer_Test is Bridge_Initializer { ) }) ); - // L2StandardBridge - contracts.push( - InitializeableContract({ - name: "L2StandardBridge", - target: address(l2StandardBridge), - initCalldata: abi.encodeCall(l2StandardBridge.initialize, (l1StandardBridge)) - }) - ); - // L2StandardBridgeInterop - contracts.push( - InitializeableContract({ - name: "L2StandardBridgeInterop", - target: address(l2StandardBridge), - initCalldata: abi.encodeCall(l2StandardBridge.initialize, (l1StandardBridge)) - }) - ); // L1ERC721BridgeImpl contracts.push( InitializeableContract({ @@ -320,26 +306,18 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall(l1ERC721Bridge.initialize, (l1CrossDomainMessenger, superchainConfig)) }) ); - // L2ERC721Bridge - contracts.push( - InitializeableContract({ - name: "L2ERC721Bridge", - target: address(l2ERC721Bridge), - initCalldata: abi.encodeCall(l2ERC721Bridge.initialize, (payable(address(l1ERC721Bridge)))) - }) - ); - // OptimismMintableERC20FactoryImpl + // L1OptimismMintableERC20FactoryImpl contracts.push( InitializeableContract({ - name: "OptimismMintableERC20Factory", + name: "L1OptimismMintableERC20Factory", target: deploy.mustGetAddress("OptimismMintableERC20Factory"), initCalldata: abi.encodeCall(l1OptimismMintableERC20Factory.initialize, (address(l1StandardBridge))) }) ); - // OptimismMintableERC20FactoryProxy + // L1OptimismMintableERC20FactoryProxy contracts.push( InitializeableContract({ - name: "OptimismMintableERC20FactoryProxy", + name: "L1OptimismMintableERC20FactoryProxy", target: address(l1OptimismMintableERC20Factory), initCalldata: abi.encodeCall(l1OptimismMintableERC20Factory.initialize, (address(l1StandardBridge))) }) @@ -487,7 +465,7 @@ contract Initializer_Test is Bridge_Initializer { // Then, attempt to re-initialize the contract. This should fail. (bool success, bytes memory returnData) = _contract.target.call(_contract.initCalldata); assertFalse(success); - assertEq(_extractErrorString(returnData), "Initializable: contract is already initialized"); + assertErrorString(returnData); } } @@ -509,10 +487,12 @@ contract Initializer_Test is Bridge_Initializer { real_ = bytes(nicknames[_name]).length > 0 ? nicknames[_name] : _name; } - /// @dev Extracts the revert string from returndata encoded in the form of `Error(string)`. - function _extractErrorString(bytes memory _returnData) internal pure returns (string memory error_) { - // The first 4 bytes of the return data should be the selector for `Error(string)`. If not, revert. - if (bytes4(_returnData) == 0x08c379a0) { + /// @dev Asserts the expected revert from the returndata + function assertErrorString(bytes memory _returnData) internal pure returns (string memory error_) { + if (bytes4(_returnData) == InvalidInitialization.selector) { + // do nothing as this is the correct 4byte returndata + } else if (bytes4(_returnData) == 0x08c379a0) { + // The first 4 bytes of the return data should be the selector for `Error(string)`. If not, revert. // Extract the error string from the returndata. The error string is located 68 bytes after // the pointer to `returnData`. // @@ -523,6 +503,7 @@ contract Initializer_Test is Bridge_Initializer { assembly { error_ := add(_returnData, 0x44) } + assertEq(error_, "Initializable: contract is already initialized"); } else { revert("Initializer_Test: Invalid returndata format. Expected `Error(string)`"); } From 5c54f884a4ad0b0a180044858dc595518464e684 Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Thu, 7 Nov 2024 11:53:07 -0600 Subject: [PATCH 148/451] interop: Fix AtLeastAsSafe ; Geth Mempool Filter E2E Test (#12823) * interop: Geth Mempool Filter E2E Test * use latest op-geth rc * fix AtLeastAsSafe * fix test --- go.mod | 2 +- go.sum | 4 +- op-e2e/interop/interop_test.go | 53 +++++++++++++++++++------ op-e2e/interop/supersystem.go | 22 ++++++++-- op-supervisor/supervisor/types/types.go | 28 ++++++++----- 5 files changed, 80 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 0cf83e382d24..d077cf6a8bb0 100644 --- a/go.mod +++ b/go.mod @@ -250,7 +250,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 +replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.6 //replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/go.sum b/go.sum index 3819b1e73fae..d9f532807675 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,8 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 h1:LDSP85xTczjDYMBK0mOF5mzpZifLGz1TTW/6NgQsytc= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.5/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.6 h1:VvUBIVFbnU9486CWHa9Js5XYY3o6OsdQcI8gE3XjCDE= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.6/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= diff --git a/op-e2e/interop/interop_test.go b/op-e2e/interop/interop_test.go index 65efa9b84209..302413dd98ed 100644 --- a/op-e2e/interop/interop_test.go +++ b/op-e2e/interop/interop_test.go @@ -20,12 +20,13 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + gethCore "github.com/ethereum/go-ethereum/core" gethTypes "github.com/ethereum/go-ethereum/core/types" ) // setupAndRun is a helper function that sets up a SuperSystem // which contains two L2 Chains, and two users on each chain. -func setupAndRun(t *testing.T, fn func(*testing.T, SuperSystem)) { +func setupAndRun(t *testing.T, config SuperSystemConfig, fn func(*testing.T, SuperSystem)) { recipe := interopgen.InteropDevRecipe{ L1ChainID: 900100, L2ChainIDs: []uint64{900200, 900201}, @@ -38,7 +39,7 @@ func setupAndRun(t *testing.T, fn func(*testing.T, SuperSystem)) { // create a super system from the recipe // and get the L2 IDs for use in the test - s2 := NewSuperSystem(t, &recipe, worldResources) + s2 := NewSuperSystem(t, &recipe, worldResources, config) // create two users on all L2 chains s2.AddUser("Alice") @@ -98,13 +99,16 @@ func TestInterop_IsolatedChains(t *testing.T) { expectedBalance, _ = big.NewInt(0).SetString("10000000000000000000000000", 10) require.Equal(t, expectedBalance, bobBalance) } - setupAndRun(t, test) + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) } -// TestInteropTrivial_EmitLogs tests a simple interop scenario +// TestInterop_EmitLogs tests a simple interop scenario // Chains A and B exist, but no messages are sent between them. // A contract is deployed on each chain, and logs are emitted repeatedly. -func TestInteropTrivial_EmitLogs(t *testing.T) { +func TestInterop_EmitLogs(t *testing.T) { test := func(t *testing.T, s2 SuperSystem) { ids := s2.L2IDs() chainA := ids[0] @@ -195,7 +199,10 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { requireMessage(chainB, log, types.CrossSafe, nil) } } - setupAndRun(t, test) + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) } func TestInteropBlockBuilding(t *testing.T) { @@ -271,11 +278,17 @@ func TestInteropBlockBuilding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() // Send an executing message, but with different payload. - // We expect the miner to be unable to include this tx, and confirmation to thus time out. - _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload) - require.NotNil(t, err) - require.ErrorIs(t, err, ctx.Err()) - require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded) + if s2.(*interopE2ESystem).config.mempoolFiltering { + // We expect the traqnsaction to be filtered out by the mempool if mempool filtering is enabled. + // ExecuteMessage the ErrTxFilteredOut error is checked when sending the tx. + _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload, gethCore.ErrTxFilteredOut) + require.ErrorContains(t, err, gethCore.ErrTxFilteredOut.Error()) + } else { + // We expect the miner to be unable to include this tx, and confirmation to thus time out, if mempool filtering is disabled. + _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload, nil) + require.ErrorIs(t, err, ctx.Err()) + require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded) + } } t.Log("Testing valid message now") @@ -284,11 +297,25 @@ func TestInteropBlockBuilding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() // Send an executing message with the correct identifier / payload - rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload) + rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload, nil) require.NoError(t, err, "expecting tx to be confirmed") t.Logf("confirmed executing msg in block %s", rec.BlockNumber) } t.Log("Done") } - setupAndRun(t, test) + + t.Run("without mempool filtering", func(t *testing.T) { + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) + }) + + t.Run("with mempool filtering", func(t *testing.T) { + config := SuperSystemConfig{ + mempoolFiltering: true, + } + // run again with mempool filtering to observe the behavior of the mempool filter + setupAndRun(t, config, test) + }) } diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index f18c7475073e..de7e25bb9c7b 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -114,14 +114,18 @@ type SuperSystem interface { msgIdentifier supervisortypes.Identifier, target common.Address, message []byte, + expectedError error, ) (*types.Receipt, error) // Access a contract on a network by name Contract(network string, contractName string) interface{} } +type SuperSystemConfig struct { + mempoolFiltering bool +} // NewSuperSystem creates a new SuperSystem from a recipe. It creates an interopE2ESystem. -func NewSuperSystem(t *testing.T, recipe *interopgen.InteropDevRecipe, w worldResourcePaths) SuperSystem { - s2 := &interopE2ESystem{recipe: recipe} +func NewSuperSystem(t *testing.T, recipe *interopgen.InteropDevRecipe, w worldResourcePaths, config SuperSystemConfig) SuperSystem { + s2 := &interopE2ESystem{recipe: recipe, config: &config} s2.prepare(t, w) return s2 } @@ -144,6 +148,7 @@ type interopE2ESystem struct { l2GethClients map[string]*ethclient.Client supervisor *supervisor.SupervisorService superClient *sources.SupervisorClient + config *SuperSystemConfig } // l2Set is a set of resources for an L2 chain @@ -263,6 +268,7 @@ func (s *interopE2ESystem) newGethForL2(id string, l2Out *interopgen.L2Output) * l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath, func(ethCfg *ethconfig.Config, nodeCfg *gn.Config) error { ethCfg.InteropMessageRPC = s.supervisor.RPC() + ethCfg.InteropMempoolFiltering = s.config.mempoolFiltering return nil }) require.NoError(s.t, err) @@ -721,6 +727,10 @@ func (s *interopE2ESystem) SendL2Tx( newApply) } +// ExecuteMessage calls the CrossL2Inbox executeMessage function +// it uses the L2's chain ID, username key, and geth client. +// expectedError represents the error returned by `ExecuteMessage` if it is expected. +// the returned err is related to `WaitMined` func (s *interopE2ESystem) ExecuteMessage( ctx context.Context, id string, @@ -728,6 +738,7 @@ func (s *interopE2ESystem) ExecuteMessage( msgIdentifier supervisortypes.Identifier, target common.Address, message []byte, + expectedError error, ) (*types.Receipt, error) { secret := s.UserKey(id, sender) auth, err := bind.NewKeyedTransactorWithChainID(&secret, s.l2s[id].chainID) @@ -746,7 +757,12 @@ func (s *interopE2ESystem) ExecuteMessage( ChainId: msgIdentifier.ChainID.ToBig(), } tx, err := contract.InboxTransactor.ExecuteMessage(auth, identifier, target, message) - require.NoError(s.t, err) + if expectedError != nil { + require.ErrorContains(s.t, err, expectedError.Error()) + return nil, err + } else { + require.NoError(s.t, err) + } s.logger.Info("Executing message", "tx", tx.Hash(), "to", tx.To(), "target", target, "data", hexutil.Bytes(tx.Data())) return bind.WaitMined(ctx, s.L2GethClient(id), tx) } diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index 381d1fc3cbda..8f2b32bb227a 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -131,19 +131,27 @@ func (lvl *SafetyLevel) UnmarshalText(text []byte) error { } // AtLeastAsSafe returns true if the receiver is at least as safe as the other SafetyLevel. +// Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest. func (lvl *SafetyLevel) AtLeastAsSafe(min SafetyLevel) bool { - switch min { - case Invalid: - return true - case CrossUnsafe: - return *lvl != Invalid - case CrossSafe: - return *lvl == CrossSafe || *lvl == Finalized - case Finalized: - return *lvl == Finalized - default: + relativeSafety := map[SafetyLevel]int{ + Invalid: 0, + LocalUnsafe: 1, + LocalSafe: 2, + CrossUnsafe: 3, + CrossSafe: 4, + Finalized: 5, + } + // if either level is not recognized, return false + _, ok := relativeSafety[*lvl] + if !ok { + return false + } + _, ok = relativeSafety[min] + if !ok { return false } + // compare the relative safety levels to determine if the receiver is at least as safe as the other + return relativeSafety[*lvl] >= relativeSafety[min] } const ( From 63b4af701c94b0c923a51a02ae8f31b43f22b557 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 8 Nov 2024 01:48:35 +0700 Subject: [PATCH 149/451] op-supervisor: do not return zeroed included-in block (#12842) * op-supervisor: do not return zeroed included-in block * op-supervisor: fix tests, implement review suggestion --- .../supervisor/backend/cross/worker.go | 2 +- .../supervisor/backend/db/logs/db.go | 9 ++++---- .../supervisor/backend/db/logs/db_test.go | 22 ++++++++++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/op-supervisor/supervisor/backend/cross/worker.go b/op-supervisor/supervisor/backend/cross/worker.go index a555a5f11b13..689b89829e4b 100644 --- a/op-supervisor/supervisor/backend/cross/worker.go +++ b/op-supervisor/supervisor/backend/cross/worker.go @@ -71,7 +71,7 @@ func (s *Worker) worker() { return } if errors.Is(err, types.ErrFuture) { - s.log.Debug("Worker awaits more data", "err", err) + s.log.Debug("Worker awaits additional blocks", "err", err) } else { s.log.Warn("Failed to process work", "err", err) } diff --git a/op-supervisor/supervisor/backend/db/logs/db.go b/op-supervisor/supervisor/backend/db/logs/db.go index 5509c5e61df8..64e61c6bebf7 100644 --- a/op-supervisor/supervisor/backend/db/logs/db.go +++ b/op-supervisor/supervisor/backend/db/logs/db.go @@ -280,6 +280,11 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ defer db.rwLock.RUnlock() db.log.Trace("Checking for log", "blockNum", blockNum, "logIdx", logIdx, "hash", logHash) + // Hot-path: check if we have the block + if db.lastEntryContext.hasCompleteBlock() && db.lastEntryContext.blockNum < blockNum { + return types.BlockSeal{}, types.ErrFuture + } + evtHash, iter, err := db.findLogInfo(blockNum, logIdx) if err != nil { return types.BlockSeal{}, err // may be ErrConflict if the block does not have as many logs @@ -306,10 +311,6 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ if err == nil { panic("expected iterator to stop with error") } - if errors.Is(err, types.ErrFuture) { - // Log is known, but as part of an unsealed block. - return types.BlockSeal{}, nil - } if errors.Is(err, types.ErrStop) { h, n, _ := iter.SealedBlock() timestamp, _ := iter.SealedTimestamp() diff --git a/op-supervisor/supervisor/backend/db/logs/db_test.go b/op-supervisor/supervisor/backend/db/logs/db_test.go index 9e7e9c51a68f..5e7c22c89939 100644 --- a/op-supervisor/supervisor/backend/db/logs/db_test.go +++ b/op-supervisor/supervisor/backend/db/logs/db_test.go @@ -582,6 +582,8 @@ func TestAddDependentLog(t *testing.T) { require.NoError(t, db.lastEntryContext.forceBlock(bl15, 5000)) err := db.AddLog(createHash(1), bl15, 0, &execMsg) require.NoError(t, err) + bl16 := eth.BlockID{Hash: createHash(16), Number: 16} + require.NoError(t, db.SealBlock(bl15.Hash, bl16, 5002)) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 16, 0, createHash(1), execMsg) @@ -602,6 +604,8 @@ func TestAddDependentLog(t *testing.T) { require.Equal(t, m.entryCount, int64(searchCheckpointFrequency+2)) err := db.AddLog(createHash(1), bl16, 0, &execMsg) require.NoError(t, err) + bl17 := eth.BlockID{Hash: createHash(17), Number: 17} + require.NoError(t, db.SealBlock(bl16.Hash, bl17, 5002)) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 16, 0, createHash(9)) @@ -632,6 +636,8 @@ func TestAddDependentLog(t *testing.T) { // 260 = executing message check require.Equal(t, int64(261), m.entryCount) db.debugTip() + bl16 := eth.BlockID{Hash: createHash(16), Number: 16} + require.NoError(t, db.SealBlock(bl15.Hash, bl16, 5001)) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 16, 251, createHash(9)) @@ -661,6 +667,8 @@ func TestAddDependentLog(t *testing.T) { // 260 = executing message check db.debugTip() require.Equal(t, int64(261), m.entryCount) + bl16 := eth.BlockID{Hash: createHash(16), Number: 16} + require.NoError(t, db.SealBlock(bl15.Hash, bl16, 5001)) }, func(t *testing.T, db *DB, m *stubMetrics) { requireContains(t, db, 16, 252, createHash(9)) @@ -689,8 +697,8 @@ func TestContains(t *testing.T) { requireContains(t, db, 51, 0, createHash(1)) requireContains(t, db, 51, 1, createHash(3)) requireContains(t, db, 51, 2, createHash(2)) - requireContains(t, db, 53, 0, createHash(1)) - requireContains(t, db, 53, 1, createHash(3)) + requireFuture(t, db, 53, 0, createHash(1)) + requireFuture(t, db, 53, 1, createHash(3)) // 52 was sealed as empty requireConflicts(t, db, 52, 0, createHash(1)) @@ -1039,7 +1047,7 @@ func TestRewind(t *testing.T) { requireContains(t, db, 51, 1, createHash(2)) requireContains(t, db, 52, 0, createHash(3)) // Still have the pending log of unsealed block if the rewind to unknown sealed block fails - requireContains(t, db, 53, 0, createHash(4)) + requireFuture(t, db, 53, 0, createHash(4)) }) }) @@ -1055,8 +1063,9 @@ func TestRewind(t *testing.T) { require.ErrorIs(t, db.Rewind(25), types.ErrSkipped) }, func(t *testing.T, db *DB, m *stubMetrics) { - requireContains(t, db, 51, 0, createHash(1)) - requireContains(t, db, 51, 0, createHash(1)) + // block 51 is not sealed yet + requireFuture(t, db, 51, 0, createHash(1)) + requireFuture(t, db, 51, 0, createHash(1)) }) }) @@ -1189,7 +1198,8 @@ func TestRewind(t *testing.T) { // try to add a log to 17, on top of 16 err = db.AddLog(createHash(42), bl16, 0, nil) require.NoError(t, err) - requireContains(t, db, 17, 0, createHash(42)) + // not sealed yet + requireFuture(t, db, 17, 0, createHash(42)) }) }) } From 0ff55ddee70efc2d6a5695f92cb877ca947eb94a Mon Sep 17 00:00:00 2001 From: Zach Howard Date: Thu, 7 Nov 2024 14:03:44 -0500 Subject: [PATCH 150/451] op-conductor: adds miner_setMaxDASize to conductor execution proxy (#12869) --- op-conductor/conductor/service.go | 5 +++ op-conductor/rpc/api.go | 13 +++++-- op-conductor/rpc/excecution_miner_proxy.go | 40 ++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 op-conductor/rpc/excecution_miner_proxy.go diff --git a/op-conductor/conductor/service.go b/op-conductor/conductor/service.go index 565096a9b45b..89656fd727aa 100644 --- a/op-conductor/conductor/service.go +++ b/op-conductor/conductor/service.go @@ -247,6 +247,11 @@ func (oc *OpConductor) initRPCServer(ctx context.Context) error { Namespace: conductorrpc.ExecutionRPCNamespace, Service: executionProxy, }) + execMinerProxy := conductorrpc.NewExecutionMinerProxyBackend(oc.log, oc, execClient) + server.AddAPI(rpc.API{ + Namespace: conductorrpc.ExecutionMinerRPCNamespace, + Service: execMinerProxy, + }) nodeClient, err := dial.DialRollupClientWithTimeout(ctx, 1*time.Minute, oc.log, oc.cfg.NodeRPC) if err != nil { diff --git a/op-conductor/rpc/api.go b/op-conductor/rpc/api.go index eafec9ac304b..a8b62e3b74fd 100644 --- a/op-conductor/rpc/api.go +++ b/op-conductor/rpc/api.go @@ -4,6 +4,7 @@ import ( "context" "errors" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-conductor/consensus" @@ -61,13 +62,19 @@ type API interface { CommitUnsafePayload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error } -// ExecutionProxyAPI defines the methods proxied to the execution rpc backend +// ExecutionProxyAPI defines the methods proxied to the execution 'eth_' rpc backend // This should include all methods that are called by op-batcher or op-proposer type ExecutionProxyAPI interface { GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) } -// NodeProxyAPI defines the methods proxied to the node rpc backend +// ExecutionMinerProxyAPI defines the methods proxied to the execution 'miner_' rpc backend +// This should include all methods that are called by op-batcher or op-proposer +type ExecutionMinerProxyAPI interface { + SetMaxDASize(ctx context.Context, maxTxSize hexutil.Big, maxBlockSize hexutil.Big) bool +} + +// NodeProxyAPI defines the methods proxied to the node 'optimism_' rpc backend // This should include all methods that are called by op-batcher or op-proposer type NodeProxyAPI interface { OutputAtBlock(ctx context.Context, blockNumString string) (*eth.OutputResponse, error) @@ -75,7 +82,7 @@ type NodeProxyAPI interface { RollupConfig(ctx context.Context) (*rollup.Config, error) } -// NodeProxyAPI defines the methods proxied to the node rpc backend +// NodeAdminProxyAPI defines the methods proxied to the node 'admin_' rpc backend // This should include all methods that are called by op-batcher or op-proposer type NodeAdminProxyAPI interface { SequencerActive(ctx context.Context) (bool, error) diff --git a/op-conductor/rpc/excecution_miner_proxy.go b/op-conductor/rpc/excecution_miner_proxy.go new file mode 100644 index 000000000000..9594e790b322 --- /dev/null +++ b/op-conductor/rpc/excecution_miner_proxy.go @@ -0,0 +1,40 @@ +package rpc + +import ( + "context" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" +) + +var ExecutionMinerRPCNamespace = "miner" + +// ExecutionMinerProxyBackend implements an execution rpc proxy with a leadership check before each call. +type ExecutionMinerProxyBackend struct { + log log.Logger + con conductor + client *ethclient.Client +} + +var _ ExecutionMinerProxyAPI = (*ExecutionMinerProxyBackend)(nil) + +func NewExecutionMinerProxyBackend(log log.Logger, con conductor, client *ethclient.Client) *ExecutionMinerProxyBackend { + return &ExecutionMinerProxyBackend{ + log: log, + con: con, + client: client, + } +} + +func (api *ExecutionMinerProxyBackend) SetMaxDASize(ctx context.Context, maxTxSize hexutil.Big, maxBlockSize hexutil.Big) bool { + var result bool + if !api.con.Leader(ctx) { + return false + } + err := api.client.Client().Call(&result, "miner_setMaxDASize", maxTxSize, maxBlockSize) + if err != nil { + return false + } + return result +} From fd1e0622075e7f193547315cd2b96a8adc22da56 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 7 Nov 2024 15:09:32 -0500 Subject: [PATCH 151/451] Revert "feat: Isthmus Contracts (#12746)"git (#12870) * Revert "feat: Isthmus Contracts (#12746)" This reverts commit dad1087728ceb3021ff2c02b8e153cfc65ea7810. * temporarily reduce heavy fuzz runs --- .semgrep/rules/sol-rules.yaml | 4 +- .semgrep/tests/sol-rules.t.sol | 6 - bedrock-devnet/devnet/__init__.py | 2 +- op-chain-ops/devkeys/devkeys.go | 4 - op-chain-ops/genesis/config.go | 3 - .../testdata/test-deploy-config-full.json | 1 - op-chain-ops/interopgen/configs.go | 9 +- op-chain-ops/interopgen/deploy.go | 40 ++- op-chain-ops/interopgen/recipe.go | 13 +- .../deployer/integration_test/apply_test.go | 116 +++---- op-deployer/pkg/deployer/opcm/opchain.go | 6 +- op-deployer/pkg/deployer/pipeline/opchain.go | 2 +- op-deployer/pkg/deployer/state/intent.go | 6 - op-deployer/pkg/deployer/testutil/env.go | 22 +- op-e2e/bindings/systemconfig.go | 108 +------ op-e2e/system/gastoken/gastoken_test.go | 17 +- packages/contracts-bedrock/.gas-snapshot | 20 +- .../deploy-config/devnetL1-template.json | 1 - .../deploy-config/hardhat.json | 1 - packages/contracts-bedrock/foundry.toml | 2 +- .../contracts-bedrock/scripts/Artifacts.s.sol | 2 + .../contracts-bedrock/scripts/L2Genesis.s.sol | 275 ++++++++-------- .../scripts/deploy/ChainAssertions.sol | 61 ++-- .../scripts/deploy/Deploy.s.sol | 24 +- .../scripts/deploy/DeployConfig.s.sol | 2 - .../deploy/DeployImplementations.s.sol | 32 +- .../scripts/deploy/DeployOPChain.s.sol | 23 +- .../scripts/deploy/DeploySuperchain.s.sol | 3 +- .../scripts/libraries/Config.sol | 9 +- .../scripts/libraries/DeployUtils.sol | 10 - .../scripts/libraries/ForgeArtifacts.sol | 19 +- packages/contracts-bedrock/semver-lock.json | 128 ++++---- .../snapshots/abi/BaseFeeVault.json | 48 ++- .../CrossDomainMessengerLegacySpacer0.json | 1 + .../CrossDomainMessengerLegacySpacer1.json | 1 + .../snapshots/abi/L1Block.json | 62 ---- .../snapshots/abi/L1BlockInterop.json | 46 +-- .../snapshots/abi/L1CrossDomainMessenger.json | 2 +- .../snapshots/abi/L1ERC721Bridge.json | 16 +- .../snapshots/abi/L1FeeVault.json | 48 ++- .../snapshots/abi/L1StandardBridge.json | 8 +- .../snapshots/abi/L2CrossDomainMessenger.json | 31 ++ .../snapshots/abi/L2ERC721Bridge.json | 33 +- .../abi/L2OptimismMintableERC20Factory.json | 196 ------------ .../snapshots/abi/L2ProxyAdmin.json | 300 ------------------ .../snapshots/abi/L2StandardBridge.json | 37 ++- .../abi/L2StandardBridgeInterop.json | 32 +- .../snapshots/abi/OPContractsManager.json | 5 - .../abi/OPContractsManagerInterop.json | 5 - ...json => OptimismMintableERC20Factory.json} | 0 .../snapshots/abi/OptimismMintableERC721.json | 6 +- .../abi/OptimismMintableERC721Factory.json | 26 +- .../snapshots/abi/OptimismPortal.json | 22 +- .../snapshots/abi/OptimismPortal2.json | 40 +-- .../snapshots/abi/OptimismPortalInterop.json | 48 +-- .../snapshots/abi/SequencerFeeVault.json | 50 ++- .../snapshots/abi/SuperchainConfig.json | 31 -- .../snapshots/abi/SystemConfig.json | 84 +---- .../snapshots/abi/SystemConfigInterop.json | 132 ++------ .../CrossDomainMessengerLegacySpacer0.json | 9 + .../CrossDomainMessengerLegacySpacer1.json | 65 ++++ .../snapshots/storageLayout/L1Block.json | 7 - .../storageLayout/L1BlockInterop.json | 9 +- .../storageLayout/L1CrossDomainMessenger.json | 62 +--- .../storageLayout/L1ERC721Bridge.json | 35 +- .../storageLayout/L1StandardBridge.json | 60 ++-- .../storageLayout/L2CrossDomainMessenger.json | 41 +-- .../storageLayout/L2ERC721Bridge.json | 28 +- .../L2OptimismMintableERC20Factory.json | 30 -- .../snapshots/storageLayout/L2ProxyAdmin.json | 37 --- .../storageLayout/L2StandardBridge.json | 32 +- .../L2StandardBridgeInterop.json | 32 +- ...json => OptimismMintableERC20Factory.json} | 54 ++-- .../src/L1/L1CrossDomainMessenger.sol | 25 +- .../src/L1/L1ERC721Bridge.sol | 30 +- .../src/L1/L1OptimismMintableERC20Factory.sol | 41 --- .../src/L1/L1StandardBridge.sol | 44 +-- .../src/L1/OPContractsManager.sol | 25 +- .../src/L1/OPContractsManagerInterop.sol | 9 +- .../src/L1/OptimismPortal.sol | 18 +- .../src/L1/OptimismPortal2.sol | 41 +-- .../src/L1/OptimismPortalInterop.sol | 35 +- .../src/L1/SuperchainConfig.sol | 30 +- .../contracts-bedrock/src/L1/SystemConfig.sol | 175 +++------- .../src/L1/SystemConfigInterop.sol | 54 +++- .../L1/interfaces/IL1CrossDomainMessenger.sol | 4 - .../src/L1/interfaces/IL1ERC721Bridge.sol | 6 - .../IL1OptimismMintableERC20Factory.sol | 38 --- .../src/L1/interfaces/IL1StandardBridge.sol | 5 +- .../src/L1/interfaces/IOptimismPortal.sol | 3 +- .../src/L1/interfaces/IOptimismPortal2.sol | 4 +- .../L1/interfaces/IOptimismPortalInterop.sol | 5 +- .../src/L1/interfaces/ISuperchainConfig.sol | 4 +- .../src/L1/interfaces/ISystemConfig.sol | 22 +- .../L1/interfaces/ISystemConfigInterop.sol | 5 +- .../contracts-bedrock/src/L2/BaseFeeVault.sol | 30 +- .../contracts-bedrock/src/L2/FeeVault.sol | 91 +++--- .../src/L2/GasPriceOracle.sol | 4 +- packages/contracts-bedrock/src/L2/L1Block.sol | 151 +-------- .../src/L2/L1BlockInterop.sol | 36 ++- .../contracts-bedrock/src/L2/L1FeeVault.sol | 30 +- .../src/L2/L2CrossDomainMessenger.sol | 27 +- .../src/L2/L2ERC721Bridge.sol | 30 +- .../src/L2/L2OptimismMintableERC20Factory.sol | 25 -- .../contracts-bedrock/src/L2/L2ProxyAdmin.sol | 17 - .../src/L2/L2StandardBridge.sol | 26 +- .../src/L2/L2StandardBridgeInterop.sol | 4 +- .../src/L2/L2ToL2CrossDomainMessenger.sol | 4 +- .../src/L2/OptimismSuperchainERC20.sol | 4 +- .../src/L2/OptimismSuperchainERC20Beacon.sol | 4 +- .../src/L2/OptimismSuperchainERC20Factory.sol | 4 +- .../src/L2/SequencerFeeVault.sol | 36 +-- .../src/L2/SuperchainWETH.sol | 4 +- packages/contracts-bedrock/src/L2/WETH.sol | 4 +- .../src/L2/interfaces/IBaseFeeVault.sol | 16 +- .../src/L2/interfaces/IFeeVault.sol | 8 +- .../src/L2/interfaces/IL1Block.sol | 7 - .../src/L2/interfaces/IL1BlockInterop.sol | 12 +- .../src/L2/interfaces/IL1FeeVault.sol | 15 +- .../L2/interfaces/IL2CrossDomainMessenger.sol | 3 +- .../src/L2/interfaces/IL2ERC721Bridge.sol | 3 +- .../IL2OptimismMintableERC20Factory.sol | 36 --- .../src/L2/interfaces/IL2StandardBridge.sol | 8 +- .../interfaces/IL2StandardBridgeInterop.sol | 10 +- .../src/L2/interfaces/ISequencerFeeVault.sol | 18 +- .../src/libraries/Encoding.sol | 36 --- .../src/libraries/Predeploys.sol | 4 +- .../src/libraries/StaticConfig.sol | 58 ---- .../contracts-bedrock/src/libraries/Types.sol | 25 -- .../src/universal/CrossDomainMessenger.sol | 108 +++---- .../src/universal/ERC721Bridge.sol | 51 +-- .../OptimismMintableERC20Factory.sol | 43 ++- .../src/universal/OptimismMintableERC721.sol | 17 +- .../OptimismMintableERC721Factory.sol | 52 ++- .../src/universal/StandardBridge.sol | 70 ++-- .../interfaces/ICrossDomainMessenger.sol | 3 +- .../universal/interfaces/IERC721Bridge.sol | 1 + .../IOptimismMintableERC20Factory.sol | 2 + .../interfaces/IOptimismMintableERC721.sol | 10 +- .../IOptimismMintableERC721Factory.sol | 12 +- .../universal/interfaces/IStandardBridge.sol | 3 +- .../test/L1/OPContractsManager.t.sol | 3 +- .../test/L1/OptimismPortal.t.sol | 16 +- .../test/L1/OptimismPortal2.t.sol | 116 +------ .../test/L1/OptimismPortalInterop.t.sol | 73 +++-- .../test/L1/SuperchainConfig.t.sol | 9 +- .../test/L1/SystemConfig.t.sol | 196 ++---------- .../test/L1/SystemConfigInterop.t.sol | 17 +- .../test/L2/BaseFeeVault.t.sol | 2 +- .../test/L2/CrossDomainOwnable2.t.sol | 13 + .../test/L2/CrossDomainOwnable3.t.sol | 2 +- .../contracts-bedrock/test/L2/FeeVault.t.sol | 21 -- .../contracts-bedrock/test/L2/L1Block.t.sol | 145 --------- .../test/L2/L1BlockInterop.t.sol | 37 +-- .../test/L2/L1FeeVault.t.sol | 2 +- .../test/L2/L2CrossDomainMessenger.t.sol | 8 +- .../test/L2/L2ERC721Bridge.t.sol | 4 +- .../contracts-bedrock/test/L2/L2Genesis.t.sol | 4 +- .../test/L2/L2StandardBridge.t.sol | 10 +- .../test/L2/Predeploys.t.sol | 17 +- .../test/L2/SequencerFeeVault.t.sol | 71 +++-- .../test/invariants/SystemConfig.t.sol | 18 +- .../test/libraries/Encoding.t.sol | 11 - .../test/opcm/DeployOPChain.t.sol | 26 +- .../contracts-bedrock/test/setup/Setup.sol | 76 +---- .../OptimismMintableERC20Factory.t.sol | 10 +- .../universal/OptimismMintableERC721.t.sol | 6 +- .../OptimismMintableERC721Factory.t.sol | 9 +- .../test/universal/Specs.t.sol | 56 +--- .../test/universal/StandardBridge.t.sol | 10 - .../test/vendor/Initializable.t.sol | 79 +++-- 171 files changed, 1903 insertions(+), 3731 deletions(-) create mode 100644 packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json create mode 100644 packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json delete mode 100644 packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json delete mode 100644 packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json rename packages/contracts-bedrock/snapshots/abi/{L1OptimismMintableERC20Factory.json => OptimismMintableERC20Factory.json} (100%) create mode 100644 packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json create mode 100644 packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json delete mode 100644 packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json delete mode 100644 packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json rename packages/contracts-bedrock/snapshots/storageLayout/{L1OptimismMintableERC20Factory.json => OptimismMintableERC20Factory.json} (64%) delete mode 100644 packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol delete mode 100644 packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol delete mode 100644 packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol delete mode 100644 packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol delete mode 100644 packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol rename packages/contracts-bedrock/src/{L2 => universal}/OptimismMintableERC721Factory.sol (51%) rename packages/contracts-bedrock/src/{L2 => universal}/interfaces/IOptimismMintableERC721Factory.sol (66%) delete mode 100644 packages/contracts-bedrock/test/L2/FeeVault.t.sol rename packages/contracts-bedrock/test/{L2 => universal}/OptimismMintableERC721Factory.t.sol (89%) diff --git a/.semgrep/rules/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml index e801bd9be25f..e0d83414c4bd 100644 --- a/.semgrep/rules/sol-rules.yaml +++ b/.semgrep/rules/sol-rules.yaml @@ -131,7 +131,7 @@ rules: - pattern-not: require($ERR); - focus-metavariable: $ERR - pattern-not-regex: \"(\w+:\s[^"]+)\" - - pattern-not-regex: string\.concat\(\"(\w+:\s)\"[^)]+\) + - pattern-not-regex: string\.concat\(\"(\w+:\s[^"]+)\"\,[^"]+\) - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: @@ -152,7 +152,7 @@ rules: - pattern-not: revert $ERR(...); - focus-metavariable: $MSG - pattern-not-regex: \"(\w+:\s[^"]+)\" - - pattern-not-regex: string\.concat\(\"(\w+:\s)\"[^)]+\) + - pattern-not-regex: string\.concat\(\"(\w+:\s[^"]+)\"\,[^"]+\) - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" - pattern-not-regex: \"([a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+-[a-zA-Z0-9\s]+)\" paths: diff --git a/.semgrep/tests/sol-rules.t.sol b/.semgrep/tests/sol-rules.t.sol index be4816dc3931..3b4329f7e388 100644 --- a/.semgrep/tests/sol-rules.t.sol +++ b/.semgrep/tests/sol-rules.t.sol @@ -497,9 +497,6 @@ contract SemgrepTest__sol_style_malformed_require { ) ); - // ok: sol-style-malformed-require - require(result.length > 0, string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); - // ruleid: sol-style-malformed-require require(cond, "MyContract: "); @@ -544,9 +541,6 @@ contract SemgrepTest__sol_style_malformed_revert { ) ); - // ok: sol-style-malformed-revert - revert(string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); - // ruleid: sol-style-malformed-revert revert("MyContract: "); diff --git a/bedrock-devnet/devnet/__init__.py b/bedrock-devnet/devnet/__init__.py index 7bf5c6a2595c..70243dc790c8 100644 --- a/bedrock-devnet/devnet/__init__.py +++ b/bedrock-devnet/devnet/__init__.py @@ -24,7 +24,7 @@ log = logging.getLogger() # Global constants -FORKS = ["delta", "ecotone", "fjord", "granite", "holocene", "isthmus"] +FORKS = ["delta", "ecotone", "fjord", "granite", "holocene"] # Global environment variables DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true" diff --git a/op-chain-ops/devkeys/devkeys.go b/op-chain-ops/devkeys/devkeys.go index 1eb011b07eaa..fdb151e4e6fc 100644 --- a/op-chain-ops/devkeys/devkeys.go +++ b/op-chain-ops/devkeys/devkeys.go @@ -158,8 +158,6 @@ const ( SequencerFeeVaultRecipientRole ChainOperatorRole = 9 // SystemConfigOwner is the key that can make SystemConfig changes. SystemConfigOwner ChainOperatorRole = 10 - // SystemConfigFeeAdmin is the key that can make SystemConfigFee changes. - SystemConfigFeeAdmin ChainOperatorRole = 11 ) func (role ChainOperatorRole) String() string { @@ -186,8 +184,6 @@ func (role ChainOperatorRole) String() string { return "sequencer-fee-vault-recipient" case SystemConfigOwner: return "system-config-owner" - case SystemConfigFeeAdmin: - return "system-config-fee-admin" default: return fmt.Sprintf("unknown-operator-%d", uint64(role)) } diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 8446241a9417..7ed0a6e2f682 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -286,9 +286,6 @@ type OperatorDeployConfig struct { // BatchSenderAddress represents the initial sequencer account that authorizes batches. // Transactions sent from this account to the batch inbox address are considered valid. BatchSenderAddress common.Address `json:"batchSenderAddress"` - - // SystemConfigfeeAdmin is the address of the account that has the ability to set the fee parameters. - SystemConfigfeeAdmin common.Address `json:"systemConfigFeeAdmin"` } var _ ConfigChecker = (*OperatorDeployConfig)(nil) diff --git a/op-chain-ops/genesis/testdata/test-deploy-config-full.json b/op-chain-ops/genesis/testdata/test-deploy-config-full.json index da1e346c407b..814fff245b0d 100644 --- a/op-chain-ops/genesis/testdata/test-deploy-config-full.json +++ b/op-chain-ops/genesis/testdata/test-deploy-config-full.json @@ -22,7 +22,6 @@ "l1GenesisBlockGasLimit": "0x1c9c380", "l1GenesisBlockDifficulty": "0x1", "finalSystemOwner": "0xbcd4042de499d14e55001ccbb24a551f3b954096", - "systemConfigFeeAdmin": "0xbcd4042de499d14e55001ccbb24a551f3b954096", "superchainConfigGuardian": "0x0000000000000000000000000000000000000112", "finalizationPeriodSeconds": 2, "l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index e3fca23a629e..942588a9929c 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -71,11 +71,10 @@ func (c *SuperchainConfig) Check(log log.Logger) error { } type L2Config struct { - Deployer common.Address // account used to deploy contracts to L2 - Proposer common.Address - Challenger common.Address - SystemConfigOwner common.Address - SystemConfigFeeAdmin common.Address + Deployer common.Address // account used to deploy contracts to L2 + Proposer common.Address + Challenger common.Address + SystemConfigOwner common.Address genesis.L2InitializationConfig Prefund map[common.Address]*big.Int SaltMixer string diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 10f361b643ac..476406821ea5 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -200,27 +200,25 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) - output, err := opcm.DeployOPChainIsthmus(l1Host, opcm.DeployOPChainInputIsthmus{ - DeployOPChainInputV160: opcm.DeployOPChainInputV160{ - OpChainProxyAdminOwner: cfg.ProxyAdminOwner, - SystemConfigOwner: cfg.SystemConfigOwner, - Batcher: cfg.BatchSenderAddress, - UnsafeBlockSigner: cfg.P2PSequencerAddress, - Proposer: cfg.Proposer, - Challenger: cfg.Challenger, - BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, - BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, - L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), - OpcmProxy: superDeployment.OpcmProxy, - SaltMixer: cfg.SaltMixer, - GasLimit: cfg.GasLimit, - DisputeGameType: cfg.DisputeGameType, - DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, - DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, - DisputeSplitDepth: cfg.DisputeSplitDepth, - DisputeClockExtension: cfg.DisputeClockExtension, - DisputeMaxClockDuration: cfg.DisputeMaxClockDuration}, - SystemConfigFeeAdmin: cfg.SystemConfigFeeAdmin, + output, err := opcm.DeployOPChainV160(l1Host, opcm.DeployOPChainInputV160{ + OpChainProxyAdminOwner: cfg.ProxyAdminOwner, + SystemConfigOwner: cfg.SystemConfigOwner, + Batcher: cfg.BatchSenderAddress, + UnsafeBlockSigner: cfg.P2PSequencerAddress, + Proposer: cfg.Proposer, + Challenger: cfg.Challenger, + BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, + BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, + L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), + OpcmProxy: superDeployment.OpcmProxy, + SaltMixer: cfg.SaltMixer, + GasLimit: cfg.GasLimit, + DisputeGameType: cfg.DisputeGameType, + DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, + DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, + DisputeSplitDepth: cfg.DisputeSplitDepth, + DisputeClockExtension: cfg.DisputeClockExtension, + DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index 06bcf8bfca03..8983ee72da8e 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -178,17 +178,12 @@ func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (* if err != nil { return nil, err } - systemConfigFeeAdmin, err := addrs.Address(chainOps(devkeys.SystemConfigFeeAdmin)) - if err != nil { - return nil, err - } l2Cfg := &L2Config{ - Deployer: deployer, - Proposer: proposer, - Challenger: challenger, - SystemConfigOwner: systemConfigOwner, - SystemConfigFeeAdmin: systemConfigFeeAdmin, + Deployer: deployer, + Proposer: proposer, + Challenger: challenger, + SystemConfigOwner: systemConfigOwner, L2InitializationConfig: genesis.L2InitializationConfig{ DevDeployConfig: genesis.DevDeployConfig{ FundDevAccounts: true, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 919b717cc055..fa59c49dc8ac 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -12,7 +12,6 @@ import ( "time" altda "github.com/ethereum-optimism/optimism/op-alt-da" - "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -126,7 +125,7 @@ func TestEndToEndApply(t *testing.T) { }) require.NoError(t, err) - env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr, localArtifactsFactory, localArtifactsFactory) + env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) cg := ethClientCodeGetter(ctx, l1Client) @@ -146,7 +145,7 @@ func TestEndToEndApply(t *testing.T) { t.Run("subsequent chain", func(t *testing.T) { // create a new environment with wiped state to ensure we can continue using the // state from the previous deployment - env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr, localArtifactsFactory, localArtifactsFactory) + env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2)) require.NoError(t, deployer.ApplyPipeline( @@ -208,17 +207,7 @@ func TestApplyExistingOPCM(t *testing.T) { }) require.NoError(t, err) - // use the l2 contracts here because the v1.7.0 contracts are compatible with the v1.6.0 - // contracts and createEnv uses the same artifacts for both L1/L2 in the bundle. - env, bundle, _ := createEnv( - t, - lgr, - l1Client, - bcaster, - deployerAddr, - taggedArtifactsFactory(standard.DefaultL1ContractsTag), - taggedArtifactsFactory(standard.DefaultL2ContractsTag), - ) + env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr) intent, st := newIntent( t, @@ -390,8 +379,7 @@ func TestProofParamOverrides(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) - require.NoError(t, err) + checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) }) } } @@ -415,9 +403,9 @@ func TestInteropDeployment(t *testing.T) { chainState := st.Chains[0] depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") - require.NoError(t, checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot)) - systemConfigOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes()) - checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, systemConfigOwnerHash) + checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) + proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes()) + checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) } func TestAltDADeployment(t *testing.T) { @@ -524,7 +512,7 @@ func TestInvalidL2Genesis(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr, localArtifactsFactory, localArtifactsFactory) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis @@ -558,42 +546,27 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, * loc, _ := testutil.LocalArtifacts(t) - env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr, localArtifactsFactory, localArtifactsFactory) + env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis return env, bundle, intent, st } -type artifactsFactory func(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) - -func localArtifactsFactory(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { - return testutil.LocalArtifacts(t) -} - -func taggedArtifactsFactory(tag string) artifactsFactory { - return func(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { - return testutil.ArtifactsFromURL(t, fmt.Sprintf("tag://%s", tag)) - } -} - func createEnv( t *testing.T, lgr log.Logger, l1Client *ethclient.Client, bcaster broadcaster.Broadcaster, deployerAddr common.Address, - l1Factory artifactsFactory, - l2Factory artifactsFactory, ) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) { - _, l1AFS := l1Factory(t) - _, l2AFS := l2Factory(t) + _, artifactsFS := testutil.LocalArtifacts(t) host, err := env.DefaultScriptHost( bcaster, lgr, deployerAddr, - l1AFS, + artifactsFS, 0, ) require.NoError(t, err) @@ -608,8 +581,8 @@ func createEnv( } bundle := pipeline.ArtifactsBundle{ - L1: l1AFS, - L2: l2AFS, + L1: artifactsFS, + L2: artifactsFS, } return env, bundle, host @@ -659,14 +632,13 @@ func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.In Eip1559Denominator: 50, Eip1559Elasticity: 6, Roles: state.ChainRoles{ - L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), - SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), - SystemConfigFeeAdmin: addrFor(t, dk, devkeys.SystemConfigFeeAdmin.Key(l1ChainID)), - UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), - Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), - Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), - Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), + L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)), + UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)), + Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)), + Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), + Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), }, } } @@ -767,20 +739,17 @@ func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, int alloc := chainState.Allocs.Data.Accounts chainIntent := intent.Chains[i] - - // First try to read the owner from the bytecode, which is the situation with the - // Isthmus L2ProxyAdmin (on this commit). - if err := checkImmutableBehindProxy(t, alloc, predeploys.ProxyAdminAddr, common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001")); err != nil { - t.Logf("Warning: Failed to check immutable behind proxy for L2ProxyAdmin, falling back to <=v1.6.0 storage check") - // If the bytecode check fails, fall back to reading the owner from storage. - // Note however that the L2ProxyAdmin owner address here (0xe59a881b2626f948f56f509f180c32428585629a) comes from the chainIntent, - // and does not actually match the `owner()` of the L2ProxyAdmin contract deployed on Sepolia (0x2FC3ffc903729a0f03966b917003800B145F67F3). - // It seems the L2 state is locally constructed rather than pulled from an L2 RPC. - var L2ProxyAdminOwner common.Hash - L2ProxyAdminOwner.SetBytes(chainIntent.Roles.L2ProxyAdminOwner.Bytes()) - checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, common.Hash{}, L2ProxyAdminOwner) - } - + checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient) + checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID))) + + // ownership slots + var addrAsSlot common.Hash + addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes()) + // slot 0 + ownerSlot := common.Hash{} + checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot) var defaultGovOwner common.Hash defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes()) checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner) @@ -801,23 +770,20 @@ type bytesMarshaler interface { Bytes() []byte } -func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) error { +func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) - return checkImmutable(t, allocations, implementationAddress, thing) + checkImmutable(t, allocations, implementationAddress, thing) } -func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) error { +func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { account, ok := allocations[implementationAddress] - if !ok { - return fmt.Errorf("%s not found in allocations", implementationAddress) - } - if len(account.Code) == 0 { - return fmt.Errorf("%s should have code", implementationAddress) - } - if !bytes.Contains(account.Code, thing.Bytes()) { - return fmt.Errorf("%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes())) - } - return nil + require.True(t, ok, "%s not found in allocations", implementationAddress) + require.NotEmpty(t, account.Code, "%s should have code", implementationAddress) + require.True( + t, + bytes.Contains(account.Code, thing.Bytes()), + "%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()), + ) } func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) { diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index c8c803d72d4f..537765ce5154 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -59,7 +59,7 @@ type opcmDeployInputV160 struct { type opcmRolesIsthmus struct { opcmRolesBase - SystemConfigFeeAdmin common.Address + FeeAdmin common.Address } type opcmDeployInputIsthmus struct { @@ -135,8 +135,8 @@ func DeployOPChainInputIsthmusDeployCalldata(input DeployOPChainInputIsthmus) an v160Data := DeployOPChainInputV160DeployCalldata(input.DeployOPChainInputV160).(opcmDeployInputV160) return opcmDeployInputIsthmus{ Roles: opcmRolesIsthmus{ - opcmRolesBase: v160Data.Roles, - SystemConfigFeeAdmin: input.SystemConfigFeeAdmin, + opcmRolesBase: v160Data.Roles, + FeeAdmin: input.SystemConfigFeeAdmin, }, opcmDeployInputBase: v160Data.opcmDeployInputBase, } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 72e4703e0503..59e5d214cf4f 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -276,7 +276,7 @@ func makeDCIIsthmus(intent *state.Intent, thisIntent *state.ChainIntent, chainID return opcm.DeployOPChainInputIsthmus{ DeployOPChainInputV160: dci, - SystemConfigFeeAdmin: thisIntent.Roles.SystemConfigFeeAdmin, + SystemConfigFeeAdmin: common.Address{'D', 'E', 'A', 'D'}, }, nil } diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 2459ddbec528..860944666e9e 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -176,8 +176,6 @@ type ChainRoles struct { SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"` - SystemConfigFeeAdmin common.Address `json:"systemConfigFeeAdmin" toml:"systemConfigFeeAdmin"` - UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"` Batcher common.Address `json:"batcher" toml:"batcher"` @@ -201,10 +199,6 @@ func (c *ChainIntent) Check() error { c.Roles.SystemConfigOwner = c.Roles.L1ProxyAdminOwner } - if c.Roles.SystemConfigFeeAdmin == emptyAddress { - return fmt.Errorf("systemConfigFeeAdmin must be set") - } - if c.Roles.L2ProxyAdminOwner == emptyAddress { return fmt.Errorf("l2ProxyAdminOwner must be set") } diff --git a/op-deployer/pkg/deployer/testutil/env.go b/op-deployer/pkg/deployer/testutil/env.go index a1731fb34b35..6b289c4503b7 100644 --- a/op-deployer/pkg/deployer/testutil/env.go +++ b/op-deployer/pkg/deployer/testutil/env.go @@ -3,35 +3,31 @@ package testutil import ( "context" "fmt" + "net/url" "path" "runtime" "testing" - "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum/log" - - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" op_service "github.com/ethereum-optimism/optimism/op-service" "github.com/stretchr/testify/require" ) -func LocalArtifacts(t *testing.T) (*artifacts.Locator, foundry.StatDirFs) { +func LocalArtifacts(t *testing.T) (*artifacts2.Locator, foundry.StatDirFs) { _, testFilename, _, ok := runtime.Caller(0) require.Truef(t, ok, "failed to get test filename") monorepoDir, err := op_service.FindMonorepoRoot(testFilename) require.NoError(t, err) artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") - return ArtifactsFromURL(t, fmt.Sprintf("file://%s", artifactsDir)) -} - -func ArtifactsFromURL(t *testing.T, artifactsURLStr string) (*artifacts.Locator, foundry.StatDirFs) { - loc := new(artifacts.Locator) - require.NoError(t, loc.UnmarshalText([]byte(artifactsURLStr))) + artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) + require.NoError(t, err) + loc := &artifacts2.Locator{ + URL: artifactsURL, + } - progressor := artifacts.LogProgressor(testlog.Logger(t, log.LevelInfo)) - artifactsFS, cleanupArtifacts, err := artifacts.Download(context.Background(), loc, progressor) + artifactsFS, cleanupArtifacts, err := artifacts2.Download(context.Background(), loc, artifacts2.NoopDownloadProgressor) require.NoError(t, err) t.Cleanup(func() { _ = cleanupArtifacts() diff --git a/op-e2e/bindings/systemconfig.go b/op-e2e/bindings/systemconfig.go index 9b845fa5e19e..a6ff5ce1480e 100644 --- a/op-e2e/bindings/systemconfig.go +++ b/op-e2e/bindings/systemconfig.go @@ -49,45 +49,15 @@ type SystemConfigAddresses struct { GasPayingToken common.Address } -// SystemConfigRoles is an auto generated low-level Go binding around an user-defined struct. -type SystemConfigRoles struct { - Owner common.Address - FeeAdmin common.Address - UnsafeBlockSigner common.Address - BatcherHash [32]byte -} - // SystemConfigMetaData contains all meta data concerning the SystemConfig contract. var SystemConfigMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"BATCH_INBOX_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"DISPUTE_GAME_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_CROSS_DOMAIN_MESSENGER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_ERC_721_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_STANDARD_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_PORTAL_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"START_BLOCK_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UNSAFE_BLOCK_SIGNER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"basefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchInbox\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batcherHash\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blobbasefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disputeGameFactory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Denominator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Elasticity\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"feeAdmin\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingToken\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"decimals_\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenName\",\"inputs\":[],\"outputs\":[{\"name\":\"name_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenSymbol\",\"inputs\":[],\"outputs\":[{\"name\":\"symbol_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_roles\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Roles\",\"components\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"feeAdmin\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_config\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]},{\"name\":\"_batchInbox\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_addresses\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Addresses\",\"components\":[{\"name\":\"l1CrossDomainMessenger\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1ERC721Bridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1StandardBridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"disputeGameFactory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismPortal\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismMintableERC20Factory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasPayingToken\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isCustomGasToken\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1CrossDomainMessenger\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1ERC721Bridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1StandardBridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maximumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"minimumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismMintableERC20Factory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismPortal\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"overhead\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"resourceConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"scalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setBatcherHash\",\"inputs\":[{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEIP1559Params\",\"inputs\":[{\"name\":\"_denominator\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_elasticity\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setFeeVaultConfig\",\"inputs\":[{\"name\":\"_type\",\"type\":\"uint8\",\"internalType\":\"enumTypes.ConfigType\"},{\"name\":\"_recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_min\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_network\",\"type\":\"uint8\",\"internalType\":\"enumTypes.WithdrawalNetwork\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfig\",\"inputs\":[{\"name\":\"_overhead\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_scalar\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfigEcotone\",\"inputs\":[{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasLimit\",\"inputs\":[{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnsafeBlockSigner\",\"inputs\":[{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"startBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"startBlock_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unsafeBlockSigner\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"ConfigUpdate\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"updateType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"UnsafeCast\",\"inputs\":[]}]", - Bin: "0x60806040523480156200001157600080fd5b506200005a6200004360017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a062000130565b60001b6000196200006a60201b6200119d1760201c565b620000646200006e565b62000156565b9055565b600054610100900460ff1615620000db5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200012e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000828210156200015157634e487b7160e01b600052601160045260246000fd5b500390565b612fd880620001666000396000f3fe608060405234801561001057600080fd5b50600436106103155760003560e01c8063a7119869116101a7578063dac6e63a116100ee578063f2fde38b11610097578063f8c68de011610071578063f8c68de01461076f578063fd32aa0f14610777578063ffa1ad741461077f57600080fd5b8063f2fde38b1461073f578063f45e65d814610752578063f68016b71461075b57600080fd5b8063e81b2c6d116100c8578063e81b2c6d1461070e578063ec70751714610717578063f2b4e6171461073757600080fd5b8063dac6e63a146106f6578063e0e2016d146106fe578063e2a3285c1461070657600080fd5b8063c4e8ddfa11610150578063cc731b021161012a578063cc731b02146105aa578063d220a9e0146106de578063d8444715146106ee57600080fd5b8063c4e8ddfa14610577578063c9b26f611461057f578063c9ff2d161461059257600080fd5b8063be4be78311610181578063be4be78314610520578063bfb14fb714610533578063c0fd4b411461056457600080fd5b8063a7119869146104fd578063b40a817c14610505578063bc49ce5f1461051857600080fd5b806330a402c01161026b578063550fcdc9116102145780638da5cb5b116101ee5780638da5cb5b146104c4578063935f029e146104e25780639b7d7f0a146104f557600080fd5b8063550fcdc9146104ac5780635d73369c146104b4578063715018a6146104bc57600080fd5b80634add321d116102455780634add321d1461043b5780634f16540b1461044357806354fd4d501461046a57600080fd5b806330a402c0146103f55780634397dfef146103fd57806348cd4cb11461043357600080fd5b806318d13918116102cd57806321326849116102a757806321326849146103b757806321d7fde5146103cf57806322b4dded146103e257600080fd5b806318d139181461039257806319f5cea8146103a75780631fd19ee1146103af57600080fd5b80630a49cb03116102fe5780630a49cb03146103625780630ae14b1b1461036a5780630c18c1621461038957600080fd5b806306c926571461031a578063078f29cf14610335575b600080fd5b610322610787565b6040519081526020015b60405180910390f35b61033d6107b5565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161032c565b61033d6107ee565b630bebc2005b60405167ffffffffffffffff909116815260200161032c565b61032260655481565b6103a56103a03660046128f3565b61081e565b005b610322610832565b61033d61085d565b6103bf610887565b604051901515815260200161032c565b6103a56103dd366004612929565b6108c6565b6103a56103f0366004612b4c565b6108dc565b61033d610d21565b610405610d4b565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260ff90911660208301520161032c565b610322610d5f565b610370610d8f565b6103227f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c0881565b60408051808201909152600c81527f322e332e302d626574612e36000000000000000000000000000000000000000060208201525b60405161032c9190612c93565b61049f610db5565b610322610dbf565b6103a5610dea565b60335473ffffffffffffffffffffffffffffffffffffffff1661033d565b6103a56104f0366004612ca6565b610dfe565b61033d610e10565b61033d610e40565b6103a5610513366004612cc8565b610e70565b610322610e81565b6103a561052e366004612ce3565b610eac565b60685461054f9068010000000000000000900463ffffffff1681565b60405163ffffffff909116815260200161032c565b6103a5610572366004612929565b610f80565b61033d610f92565b6103a561058d366004612d3c565b610fc2565b606a5461054f90640100000000900463ffffffff1681565b61066e6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c08101825260695463ffffffff8082168352640100000000820460ff9081166020850152650100000000008304169383019390935266010000000000008104831660608301526a0100000000000000000000810490921660808201526e0100000000000000000000000000009091046fffffffffffffffffffffffffffffffff1660a082015290565b60405161032c9190600060c08201905063ffffffff80845116835260ff602085015116602084015260ff6040850151166040840152806060850151166060840152806080850151166080840152506fffffffffffffffffffffffffffffffff60a08401511660a083015292915050565b606a5461054f9063ffffffff1681565b61049f610fd3565b61033d610fdd565b61032261100d565b610322611038565b61032260675481565b60685461054f906c01000000000000000000000000900463ffffffff1681565b61033d611063565b6103a561074d3660046128f3565b611093565b61032260665481565b6068546103709067ffffffffffffffff1681565b610322611147565b610322611172565b610322600081565b6107b260017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b81565b60006107e96107e560017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b5490565b905090565b60006107e96107e560017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b6108266111a1565b61082f81611222565b50565b6107b260017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b60006107e97f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c085490565b600080610892610d4b565b5073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141592915050565b6108ce6111a1565b6108d882826112df565b5050565b600054610100900460ff16158080156108fc5750600054600160ff909116105b806109165750303b158015610916575060005460ff166001145b6109a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610a0557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610a0d61141e565b8751610a1890611093565b610a2588606001516114bd565b610a2f87876112df565b610a38856114e5565b60408801517f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c085560208801517f5a12cb4aeab79fd7e76814330f3e0732946417b36fa088add63d298d3c0f7a3f55610ab9610ab460017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b849055565b610af0610ae760017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b60808401519055565b610b27610b1e60017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b60608401519055565b610b5e610b5560017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b60a08401519055565b610b95610b8c60017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b8351600461163b565b610bcf610bc360017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b6020840151600561163b565b610c09610bfd60017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b6040840151600661163b565b610c116116f2565b610c19611784565b610c268260c001516117ec565b610c2f84611ae0565b610c37610d8f565b67ffffffffffffffff168567ffffffffffffffff161015610cb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b8015610d1757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b60006107e97f5a12cb4aeab79fd7e76814330f3e0732946417b36fa088add63d298d3c0f7a3f5490565b600080610d56611f54565b90939092509050565b60006107e96107e560017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b6069546000906107e99063ffffffff6a0100000000000000000000820481169116612d9b565b60606107e9611fd1565b6107b260017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b610df26111a1565b610dfc6000612092565b565b610e066111a1565b6108d88282612109565b60006107e96107e560017fa04c5bb938ca6fc46d95553abf0a76345ce3e722a30bf4f74928b8e7d852320d612d84565b60006107e96107e560017f383f291819e6d54073bc9a648251d97421076bdd101933c0c022219ce9580637612d84565b610e786111a1565b61082f816114e5565b6107b260017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b610eb4610d21565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f53797374656d436f6e6669673a2063616c6c6572206973206e6f74207468652060448201527f6665652061646d696e0000000000000000000000000000000000000000000000606482015260840161099e565b610f7a848484846121df565b50505050565b610f886111a1565b6108d88282612353565b60006107e96107e560017f46adcbebc6be8ce551740c29c47c8798210f23f7f4086c41752944352568d5a8612d84565b610fca6111a1565b61082f816114bd565b60606107e9612525565b60006107e96107e560017f71ac12829d66ee73d8d95bff50b3589745ce57edae70a3fb111a2342464dc598612d84565b6107b260017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b6107b260017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b60006107e96107e560017f52322a25d9f59ea17656545543306b7aef62bc0cc53a0e65ccfa0c75b97aa907612d84565b61109b6111a1565b73ffffffffffffffffffffffffffffffffffffffff811661113e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161099e565b61082f81612092565b6107b260017f9904ba90dde5696cda05c9e0dab5cbaa0fea005ace4d11218a02ac668dad6377612d84565b6107b260017f4b6c74f9e688cb39801f2112c14a8c57232a3fc5202e1444126d4bce86eb19ad612d84565b9055565b60335473ffffffffffffffffffffffffffffffffffffffff163314610dfc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161099e565b61124b7f65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08829055565b6040805173ffffffffffffffffffffffffffffffffffffffff8316602082015260009101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060035b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516112d39190612c93565b60405180910390a35050565b606880547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000063ffffffff8581169182027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16929092176c0100000000000000000000000092851692909202919091179091557f0100000000000000000000000000000000000000000000000000000000000000602083811b67ffffffff000000001690921717606681905560655460408051938401919091528201526000906060015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060015b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516114119190612c93565b60405180910390a3505050565b600054610100900460ff166114b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161099e565b610dfc6125db565b60678190556040805160208082018490528251808303909101815290820190915260006112a2565b6114ed610d8f565b67ffffffffffffffff168167ffffffffffffffff16101561156a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b630bebc20067ffffffffffffffff821611156115e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f2068696768604482015260640161099e565b606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260026112a2565b8183556116466107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c0012163828460405160200161168f919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016116bb929190612df6565b600060405180830381600087803b1580156116d557600080fd5b505af11580156116e9573d6000803e3d6000fd5b50505050505050565b6116fa6107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c001216360074660405160200161172a91815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611756929190612df6565b600060405180830381600087803b15801561177057600080fd5b505af1158015610f7a573d6000803e3d6000fd5b6117b26107e560017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b600003610dfc57610dfc6117e760017fa11ee3ab75b40e88a0105e935d17cd36c8faee0138320d776c411291bdbbb1a0612d84565b439055565b73ffffffffffffffffffffffffffffffffffffffff81161580159061183b575073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14155b801561184c575061184a610887565b155b1561082f57601260ff168173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c59190612e4e565b60ff1614611955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f53797374656d436f6e6669673a2062616420646563696d616c73206f6620676160448201527f7320706179696e6720746f6b656e000000000000000000000000000000000000606482015260840161099e565b60006119f08273ffffffffffffffffffffffffffffffffffffffff166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa1580156119a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526119eb9190810190612e6b565b61267b565b90506000611a428373ffffffffffffffffffffffffffffffffffffffff166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa1580156119a5573d6000803e3d6000fd5b9050611a51836012848461271e565b611a596107ee565b6040805173ffffffffffffffffffffffffffffffffffffffff86811660208301526012828401526060820186905260808083018690528351808403909101815260a08301938490527fc001216300000000000000000000000000000000000000000000000000000000909352929092169163c0012163916116bb916000919060a401612df6565b8060a001516fffffffffffffffffffffffffffffffff16816060015163ffffffff161115611b90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f53797374656d436f6e6669673a206d696e206261736520666565206d7573742060448201527f6265206c657373207468616e206d617820626173650000000000000000000000606482015260840161099e565b6001816040015160ff1611611c27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f53797374656d436f6e6669673a2064656e6f6d696e61746f72206d757374206260448201527f65206c6172676572207468616e20310000000000000000000000000000000000606482015260840161099e565b6068546080820151825167ffffffffffffffff90921691611c489190612f36565b63ffffffff161115611cb6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640161099e565b6000816020015160ff1611611d4d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f53797374656d436f6e6669673a20656c6173746963697479206d756c7469706c60448201527f6965722063616e6e6f7420626520300000000000000000000000000000000000606482015260840161099e565b8051602082015163ffffffff82169160ff90911690611d6d908290612f55565b611d779190612f9f565b63ffffffff1614611e0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f53797374656d436f6e6669673a20707265636973696f6e206c6f73732077697460448201527f6820746172676574207265736f75726365206c696d6974000000000000000000606482015260840161099e565b805160698054602084015160408501516060860151608087015160a09097015163ffffffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009095169490941764010000000060ff94851602177fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000093909216929092027fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffff1617660100000000000091851691909102177fffff0000000000000000000000000000000000000000ffffffffffffffffffff166a010000000000000000000093909416929092027fffff00000000000000000000000000000000ffffffffffffffffffffffffffff16929092176e0100000000000000000000000000006fffffffffffffffffffffffffffffffff90921691909102179055565b60008080611f866107e560017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec612d84565b73ffffffffffffffffffffffffffffffffffffffff81169350905082611fc5575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee92601292509050565b60a081901c9150509091565b60606000611fdd611f54565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff82160161205657505060408051808201909152600381527f4554480000000000000000000000000000000000000000000000000000000000602082015290565b61208c6120876107e560017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764612d84565b6127ea565b91505090565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b7fff000000000000000000000000000000000000000000000000000000000000008116156121b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f53797374656d436f6e6669673a207363616c61722065786365656473206d617860448201527f2e00000000000000000000000000000000000000000000000000000000000000606482015260840161099e565b6065829055606681905560408051602081018490529081018290526000906060016113ad565b60018460098111156121f3576121f3612dc7565b14806122105750600284600981111561220e5761220e612dc7565b145b8061222c5750600384600981111561222a5761222a612dc7565b145b6122b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f53797374656d436f6e6669673a20436f6e66696754797065206973206973206e60448201527f6f74206120466565205661756c7420436f6e6669672074797065000000000000606482015260840161099e565b6122c06107ee565b73ffffffffffffffffffffffffffffffffffffffff1663c0012163856122e786868661281e565b6040516020016122f991815260200190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401612325929190612df6565b600060405180830381600087803b15801561233f57600080fd5b505af1158015610d17573d6000803e3d6000fd5b60018263ffffffff1610156123ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53797374656d436f6e6669673a2064656e6f6d696e61746f72206d757374206260448201527f65203e3d20310000000000000000000000000000000000000000000000000000606482015260840161099e565b60018163ffffffff161015612481576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53797374656d436f6e6669673a20656c6173746963697479206d75737420626560448201527f203e3d2031000000000000000000000000000000000000000000000000000000606482015260840161099e565b606a805463ffffffff83811664010000000081027fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090931691861691909117919091179091556040516000916124ee91602086811b67ffffffff0000000016909217910190815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060046113e0565b60606000612531611f54565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016125aa57505060408051808201909152600581527f4574686572000000000000000000000000000000000000000000000000000000602082015290565b61208c6120876107e560017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d612d84565b600054610100900460ff16612672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161099e565b610dfc33612092565b600060208251111561270f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f476173506179696e67546f6b656e3a20737472696e672063616e6e6f7420626560448201527f2067726561746572207468616e20333220627974657300000000000000000000606482015260840161099e565b612718826128a1565b92915050565b61278461274c60017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec612d84565b74ff000000000000000000000000000000000000000060a086901b1673ffffffffffffffffffffffffffffffffffffffff8716179055565b6127b76127b260017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d612d84565b839055565b610f7a6127e560017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764612d84565b829055565b60405160005b82811a15612800576001016127f0565b80825260208201838152600082820152505060408101604052919050565b60006affffffffffffffffffffff831115612865576040517fc4bd89a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1660a084901b60f884600181111561289557612895612dc7565b901b1717949350505050565b8051602181106128b95763ec92f9a36000526004601cfd5b9081015160209190910360031b1b90565b803573ffffffffffffffffffffffffffffffffffffffff811681146128ee57600080fd5b919050565b60006020828403121561290557600080fd5b61290e826128ca565b9392505050565b803563ffffffff811681146128ee57600080fd5b6000806040838503121561293c57600080fd5b61294583612915565b915061295360208401612915565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff811182821017156129ae576129ae61295c565b60405290565b803567ffffffffffffffff811681146128ee57600080fd5b60ff8116811461082f57600080fd5b600060c082840312156129ed57600080fd5b60405160c0810181811067ffffffffffffffff82111715612a1057612a1061295c565b604052905080612a1f83612915565b81526020830135612a2f816129cc565b60208201526040830135612a42816129cc565b6040820152612a5360608401612915565b6060820152612a6460808401612915565b608082015260a08301356fffffffffffffffffffffffffffffffff81168114612a8c57600080fd5b60a0919091015292915050565b600060e08284031215612aab57600080fd5b60405160e0810181811067ffffffffffffffff82111715612ace57612ace61295c565b604052905080612add836128ca565b8152612aeb602084016128ca565b6020820152612afc604084016128ca565b6040820152612b0d606084016128ca565b6060820152612b1e608084016128ca565b6080820152612b2f60a084016128ca565b60a0820152612b4060c084016128ca565b60c08201525092915050565b60008060008060008060008789036102a0811215612b6957600080fd5b6080811215612b7757600080fd5b50612b8061298b565b612b89896128ca565b8152612b9760208a016128ca565b6020820152612ba860408a016128ca565b6040820152606089810135908201529650612bc560808901612915565b9550612bd360a08901612915565b9450612be160c089016129b4565b9350612bf08960e08a016129db565b9250612bff6101a089016128ca565b9150612c0f896101c08a01612a99565b905092959891949750929550565b60005b83811015612c38578181015183820152602001612c20565b83811115610f7a5750506000910152565b60008151808452612c61816020860160208601612c1d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061290e6020830184612c49565b60008060408385031215612cb957600080fd5b50508035926020909101359150565b600060208284031215612cda57600080fd5b61290e826129b4565b60008060008060808587031215612cf957600080fd5b8435600a8110612d0857600080fd5b9350612d16602086016128ca565b925060408501359150606085013560028110612d3157600080fd5b939692955090935050565b600060208284031215612d4e57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612d9657612d96612d55565b500390565b600067ffffffffffffffff808316818516808303821115612dbe57612dbe612d55565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000600a8410612e2f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b83825260406020830152612e466040830184612c49565b949350505050565b600060208284031215612e6057600080fd5b815161290e816129cc565b600060208284031215612e7d57600080fd5b815167ffffffffffffffff80821115612e9557600080fd5b818401915084601f830112612ea957600080fd5b815181811115612ebb57612ebb61295c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715612f0157612f0161295c565b81604052828152876020848701011115612f1a57600080fd5b612f2b836020830160208801612c1d565b979650505050505050565b600063ffffffff808316818516808303821115612dbe57612dbe612d55565b600063ffffffff80841680612f93577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600063ffffffff80831681851681830481118215151615612fc257612fc2612d55565b0294935050505056fea164736f6c634300080f000a", + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"BATCH_INBOX_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"DISPUTE_GAME_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_CROSS_DOMAIN_MESSENGER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_ERC_721_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"L1_STANDARD_BRIDGE_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPTIMISM_PORTAL_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"START_BLOCK_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UNSAFE_BLOCK_SIGNER_SLOT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"basefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchInbox\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batcherHash\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blobbasefeeScalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disputeGameFactory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Denominator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"eip1559Elasticity\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingToken\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"decimals_\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenName\",\"inputs\":[],\"outputs\":[{\"name\":\"name_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gasPayingTokenSymbol\",\"inputs\":[],\"outputs\":[{\"name\":\"symbol_\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_config\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]},{\"name\":\"_batchInbox\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_addresses\",\"type\":\"tuple\",\"internalType\":\"structSystemConfig.Addresses\",\"components\":[{\"name\":\"l1CrossDomainMessenger\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1ERC721Bridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1StandardBridge\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"disputeGameFactory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismPortal\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"optimismMintableERC20Factory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasPayingToken\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isCustomGasToken\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1CrossDomainMessenger\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1ERC721Bridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"l1StandardBridge\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maximumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"minimumGasLimit\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismMintableERC20Factory\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"optimismPortal\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"overhead\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"resourceConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIResourceMetering.ResourceConfig\",\"components\":[{\"name\":\"maxResourceLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"elasticityMultiplier\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"baseFeeMaxChangeDenominator\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"minimumBaseFee\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"systemTxMaxGas\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"maximumBaseFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"scalar\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setBatcherHash\",\"inputs\":[{\"name\":\"_batcherHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEIP1559Params\",\"inputs\":[{\"name\":\"_denominator\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_elasticity\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfig\",\"inputs\":[{\"name\":\"_overhead\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_scalar\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasConfigEcotone\",\"inputs\":[{\"name\":\"_basefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_blobbasefeeScalar\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setGasLimit\",\"inputs\":[{\"name\":\"_gasLimit\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnsafeBlockSigner\",\"inputs\":[{\"name\":\"_unsafeBlockSigner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"startBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"startBlock_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unsafeBlockSigner\",\"inputs\":[],\"outputs\":[{\"name\":\"addr_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"ConfigUpdate\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"updateType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", } // SystemConfigABI is the input ABI used to generate the binding from. // Deprecated: Use SystemConfigMetaData.ABI instead. var SystemConfigABI = SystemConfigMetaData.ABI -// SystemConfigBin is the compiled bytecode used for deploying new contracts. -// Deprecated: Use SystemConfigMetaData.Bin instead. -var SystemConfigBin = SystemConfigMetaData.Bin - -// DeploySystemConfig deploys a new Ethereum contract, binding an instance of SystemConfig to it. -func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SystemConfig, error) { - parsed, err := SystemConfigMetaData.GetAbi() - if err != nil { - return common.Address{}, nil, nil, err - } - if parsed == nil { - return common.Address{}, nil, nil, errors.New("GetABI returned nil") - } - - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SystemConfigBin), backend) - if err != nil { - return common.Address{}, nil, nil, err - } - return address, tx, &SystemConfig{SystemConfigCaller: SystemConfigCaller{contract: contract}, SystemConfigTransactor: SystemConfigTransactor{contract: contract}, SystemConfigFilterer: SystemConfigFilterer{contract: contract}}, nil -} - // SystemConfig is an auto generated Go binding around an Ethereum contract. type SystemConfig struct { SystemConfigCaller // Read-only binding to the contract @@ -757,37 +727,6 @@ func (_SystemConfig *SystemConfigCallerSession) Eip1559Elasticity() (uint32, err return _SystemConfig.Contract.Eip1559Elasticity(&_SystemConfig.CallOpts) } -// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. -// -// Solidity: function feeAdmin() view returns(address addr_) -func (_SystemConfig *SystemConfigCaller) FeeAdmin(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _SystemConfig.contract.Call(opts, &out, "feeAdmin") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. -// -// Solidity: function feeAdmin() view returns(address addr_) -func (_SystemConfig *SystemConfigSession) FeeAdmin() (common.Address, error) { - return _SystemConfig.Contract.FeeAdmin(&_SystemConfig.CallOpts) -} - -// FeeAdmin is a free data retrieval call binding the contract method 0x30a402c0. -// -// Solidity: function feeAdmin() view returns(address addr_) -func (_SystemConfig *SystemConfigCallerSession) FeeAdmin() (common.Address, error) { - return _SystemConfig.Contract.FeeAdmin(&_SystemConfig.CallOpts) -} - // GasLimit is a free data retrieval call binding the contract method 0xf68016b7. // // Solidity: function gasLimit() view returns(uint64) @@ -1391,25 +1330,25 @@ func (_SystemConfig *SystemConfigCallerSession) Version() (string, error) { return _SystemConfig.Contract.Version(&_SystemConfig.CallOpts) } -// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. +// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. // -// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.contract.Transact(opts, "initialize", _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) +// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.contract.Transact(opts, "initialize", _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) } -// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. +// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. // -// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigSession) Initialize(_roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) +// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigSession) Initialize(_owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) } -// Initialize is a paid mutator transaction binding the contract method 0x22b4dded. +// Initialize is a paid mutator transaction binding the contract method 0xdb9040fa. // -// Solidity: function initialize((address,address,address,bytes32) _roles, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, uint64 _gasLimit, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() -func (_SystemConfig *SystemConfigTransactorSession) Initialize(_roles SystemConfigRoles, _basefeeScalar uint32, _blobbasefeeScalar uint32, _gasLimit uint64, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { - return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _roles, _basefeeScalar, _blobbasefeeScalar, _gasLimit, _config, _batchInbox, _addresses) +// Solidity: function initialize(address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner, (uint32,uint8,uint8,uint32,uint32,uint128) _config, address _batchInbox, (address,address,address,address,address,address,address) _addresses) returns() +func (_SystemConfig *SystemConfigTransactorSession) Initialize(_owner common.Address, _basefeeScalar uint32, _blobbasefeeScalar uint32, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address, _config IResourceMeteringResourceConfig, _batchInbox common.Address, _addresses SystemConfigAddresses) (*types.Transaction, error) { + return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _basefeeScalar, _blobbasefeeScalar, _batcherHash, _gasLimit, _unsafeBlockSigner, _config, _batchInbox, _addresses) } // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. @@ -1475,27 +1414,6 @@ func (_SystemConfig *SystemConfigTransactorSession) SetEIP1559Params(_denominato return _SystemConfig.Contract.SetEIP1559Params(&_SystemConfig.TransactOpts, _denominator, _elasticity) } -// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. -// -// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() -func (_SystemConfig *SystemConfigTransactor) SetFeeVaultConfig(opts *bind.TransactOpts, _type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { - return _SystemConfig.contract.Transact(opts, "setFeeVaultConfig", _type, _recipient, _min, _network) -} - -// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. -// -// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() -func (_SystemConfig *SystemConfigSession) SetFeeVaultConfig(_type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { - return _SystemConfig.Contract.SetFeeVaultConfig(&_SystemConfig.TransactOpts, _type, _recipient, _min, _network) -} - -// SetFeeVaultConfig is a paid mutator transaction binding the contract method 0xbe4be783. -// -// Solidity: function setFeeVaultConfig(uint8 _type, address _recipient, uint256 _min, uint8 _network) returns() -func (_SystemConfig *SystemConfigTransactorSession) SetFeeVaultConfig(_type uint8, _recipient common.Address, _min *big.Int, _network uint8) (*types.Transaction, error) { - return _SystemConfig.Contract.SetFeeVaultConfig(&_SystemConfig.TransactOpts, _type, _recipient, _min, _network) -} - // SetGasConfig is a paid mutator transaction binding the contract method 0x935f029e. // // Solidity: function setGasConfig(uint256 _overhead, uint256 _scalar) returns() diff --git a/op-e2e/system/gastoken/gastoken_test.go b/op-e2e/system/gastoken/gastoken_test.go index d83592f28b01..7e03b19d3930 100644 --- a/op-e2e/system/gastoken/gastoken_test.go +++ b/op-e2e/system/gastoken/gastoken_test.go @@ -159,21 +159,18 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System require.NoError(t, err) // Get existing parameters from SystemConfigProxy contract - roles := bindings.SystemConfigRoles{} - roles.Owner, err = systemConfig.Owner(&bind.CallOpts{}) - require.NoError(t, err) - roles.FeeAdmin, err = systemConfig.FeeAdmin(&bind.CallOpts{}) - require.NoError(t, err) - roles.UnsafeBlockSigner, err = systemConfig.UnsafeBlockSigner(&bind.CallOpts{}) - require.NoError(t, err) - roles.BatcherHash, err = systemConfig.BatcherHash(&bind.CallOpts{}) + owner, err := systemConfig.Owner(&bind.CallOpts{}) require.NoError(t, err) basefeeScalar, err := systemConfig.BasefeeScalar(&bind.CallOpts{}) require.NoError(t, err) blobbasefeeScalar, err := systemConfig.BlobbasefeeScalar(&bind.CallOpts{}) require.NoError(t, err) + batcherHash, err := systemConfig.BatcherHash(&bind.CallOpts{}) + require.NoError(t, err) gasLimit, err := systemConfig.GasLimit(&bind.CallOpts{}) require.NoError(t, err) + unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(&bind.CallOpts{}) + require.NoError(t, err) resourceConfig, err := systemConfig.ResourceConfig(&bind.CallOpts{}) require.NoError(t, err) batchInbox, err := systemConfig.BatchInbox(&bind.CallOpts{}) @@ -231,10 +228,12 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System waitForTx(t, tx, err, l1Client) // Reinitialise with existing initializer values but with custom gas token set - tx, err = systemConfig.Initialize(deployerOpts, roles, + tx, err = systemConfig.Initialize(deployerOpts, owner, basefeeScalar, blobbasefeeScalar, + batcherHash, gasLimit, + unsafeBlockSigner, resourceConfig, batchInbox, addresses) diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/.gas-snapshot index 39eba667c79e..1b1a01905ba5 100644 --- a/packages/contracts-bedrock/.gas-snapshot +++ b/packages/contracts-bedrock/.gas-snapshot @@ -4,14 +4,14 @@ GasBenchMark_L1BlockInterop_SetValuesInterop:test_setL1BlockValuesInterop_benchm GasBenchMark_L1BlockInterop_SetValuesInterop_Warm:test_setL1BlockValuesInterop_benchmark() (gas: 5099) GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (gas: 158531) GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7597) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 367239) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2965379) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 560245) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4072288) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 462746) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3508450) -GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 70492) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369280) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967420) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564398) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076613) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467098) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512802) +GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72664) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973) -GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68366) -GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68930) -GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155655) \ No newline at end of file +GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68422) +GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68986) +GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155610) \ No newline at end of file diff --git a/packages/contracts-bedrock/deploy-config/devnetL1-template.json b/packages/contracts-bedrock/deploy-config/devnetL1-template.json index bec9c6631793..2f275f46c4f6 100644 --- a/packages/contracts-bedrock/deploy-config/devnetL1-template.json +++ b/packages/contracts-bedrock/deploy-config/devnetL1-template.json @@ -27,7 +27,6 @@ "sequencerFeeVaultWithdrawalNetwork": 0, "proxyAdminOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "finalSystemOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", - "systemConfigFeeAdmin": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "superchainConfigGuardian": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", "finalizationPeriodSeconds": 2, "fundDevAccounts": true, diff --git a/packages/contracts-bedrock/deploy-config/hardhat.json b/packages/contracts-bedrock/deploy-config/hardhat.json index 9009f9157d3e..965d403d4d82 100644 --- a/packages/contracts-bedrock/deploy-config/hardhat.json +++ b/packages/contracts-bedrock/deploy-config/hardhat.json @@ -1,6 +1,5 @@ { "finalSystemOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "systemConfigFeeAdmin": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", "superchainConfigGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "l1StartingBlockTag": "earliest", diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 78c932ac7eea..b2ea03b86538 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -76,8 +76,8 @@ runs = 256 depth = 32 [profile.ciheavy] -# temp reduce fuzz runs for 1 pr # fuzz = { runs = 20000 } +# temporary reduce fuzz runs to unblock CI fuzz = { runs = 200 } [profile.ciheavy.invariant] diff --git a/packages/contracts-bedrock/scripts/Artifacts.s.sol b/packages/contracts-bedrock/scripts/Artifacts.s.sol index c516d2cf55e9..3ed021ea9dbb 100644 --- a/packages/contracts-bedrock/scripts/Artifacts.s.sol +++ b/packages/contracts-bedrock/scripts/Artifacts.s.sol @@ -47,6 +47,7 @@ abstract contract Artifacts { /// @notice Setup function. The arguments here function setUp() public virtual { deploymentOutfile = Config.deploymentOutfile(); + console.log("Writing artifact to %s", deploymentOutfile); ForgeArtifacts.ensurePath(deploymentOutfile); uint256 chainId = Config.chainID(); @@ -182,6 +183,7 @@ abstract contract Artifacts { /// @param _name The name of the deployment. /// @param _deployed The address of the deployment. function save(string memory _name, address _deployed) public { + console.log("Saving %s: %s", _name, _deployed); if (bytes(_name).length == 0) { revert InvalidDeployment("EmptyName"); } diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 098cb5e390f4..207c59732146 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -8,28 +8,27 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; // Scripts import { Deployer } from "scripts/deploy/Deployer.sol"; import { Config, OutputMode, OutputModeUtils, Fork, ForkUtils, LATEST_FORK } from "scripts/libraries/Config.sol"; -import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { Process } from "scripts/libraries/Process.sol"; import { SetPreinstalls } from "scripts/SetPreinstalls.s.sol"; - -// Contracts -import { SequencerFeeVault } from "src/L2/SequencerFeeVault.sol"; -import { BaseFeeVault } from "src/L2/BaseFeeVault.sol"; -import { L1FeeVault } from "src/L2/L1FeeVault.sol"; -import { OptimismSuperchainERC20Beacon } from "src/L2/OptimismSuperchainERC20Beacon.sol"; -import { OptimismMintableERC721Factory } from "src/L2/OptimismMintableERC721Factory.sol"; -import { GovernanceToken } from "src/governance/GovernanceToken.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; -import { Constants } from "src/libraries/Constants.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; import { Types } from "src/libraries/Types.sol"; // Interfaces +import { ISequencerFeeVault } from "src/L2/interfaces/ISequencerFeeVault.sol"; +import { IBaseFeeVault } from "src/L2/interfaces/IBaseFeeVault.sol"; +import { IL1FeeVault } from "src/L2/interfaces/IL1FeeVault.sol"; +import { IOptimismMintableERC721Factory } from "src/universal/interfaces/IOptimismMintableERC721Factory.sol"; import { IGovernanceToken } from "src/governance/interfaces/IGovernanceToken.sol"; +import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; +import { IL2StandardBridge } from "src/L2/interfaces/IL2StandardBridge.sol"; +import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; +import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMessenger.sol"; import { IGasPriceOracle } from "src/L2/interfaces/IGasPriceOracle.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; @@ -111,12 +110,7 @@ contract L2Genesis is Deployer { /// Sets the precompiles, proxies, and the implementation accounts to be `vm.dumpState` /// to generate a L2 genesis alloc. function runWithStateDump() public { - runWithOptions({ - _mode: Config.outputMode(), - _fork: Config.fork(), - _populateNetworkConfig: true, - _l1Dependencies: artifactDependencies() - }); + runWithOptions(Config.outputMode(), cfg.fork(), artifactDependencies()); } /// @notice Alias for `runWithStateDump` so that no `--sig` needs to be specified. @@ -126,72 +120,45 @@ contract L2Genesis is Deployer { /// @notice This is used by op-e2e to have a version of the L2 allocs for each upgrade. function runWithAllUpgrades() public { - console.log("L2Genesis: runWithAllUpgrades"); - runWithOptions({ - _mode: OutputMode.ALL, - _fork: LATEST_FORK, - _populateNetworkConfig: true, - _l1Dependencies: artifactDependencies() - }); + runWithOptions(OutputMode.ALL, LATEST_FORK, artifactDependencies()); } /// @notice This is used by new experimental interop deploy tooling. function runWithEnv() public { // The setUp() is skipped (since we insert a custom DeployConfig, and do not use Artifacts) deployer = makeAddr("deployer"); - runWithOptions({ - _mode: OutputMode.NONE, - _fork: Config.fork(), - _populateNetworkConfig: false, - _l1Dependencies: L1Dependencies({ + runWithOptions( + OutputMode.NONE, + Config.fork(), + L1Dependencies({ l1CrossDomainMessengerProxy: payable(vm.envAddress("L2GENESIS_L1CrossDomainMessengerProxy")), l1StandardBridgeProxy: payable(vm.envAddress("L2GENESIS_L1StandardBridgeProxy")), l1ERC721BridgeProxy: payable(vm.envAddress("L2GENESIS_L1ERC721BridgeProxy")) }) - }); + ); } /// @notice This is used by foundry tests to enable the latest fork with the /// given L1 dependencies. function runWithLatestLocal(L1Dependencies memory _l1Dependencies) public { - runWithOptions({ - _mode: OutputMode.NONE, - _fork: LATEST_FORK, - _populateNetworkConfig: true, - _l1Dependencies: _l1Dependencies - }); + runWithOptions(OutputMode.NONE, LATEST_FORK, _l1Dependencies); } /// @notice Build the L2 genesis. - /// @param _mode The mode to run the script in. - /// @param _fork The fork to build the genesis for. - /// @param _populateNetworkConfig If true, the L1 Block contract will be populated with network specific - /// configuration. Otherwise, the standard genesis will be built. - function runWithOptions( - OutputMode _mode, - Fork _fork, - bool _populateNetworkConfig, - L1Dependencies memory _l1Dependencies - ) - public - { + function runWithOptions(OutputMode _mode, Fork _fork, L1Dependencies memory _l1Dependencies) public { console.log("L2Genesis: outputMode: %s, fork: %s", _mode.toString(), _fork.toString()); vm.startPrank(deployer); vm.chainId(cfg.l2ChainID()); dealEthToPrecompiles(); setPredeployProxies(); - setPredeployImplementations(); + setPredeployImplementations(_l1Dependencies); setPreinstalls(); if (cfg.fundDevAccounts()) { fundDevAccounts(); } vm.stopPrank(); - if (_populateNetworkConfig) { - _setNetworkConfig(_l1Dependencies, cfg); - } - if (writeForkGenesisAllocs(_fork, Fork.DELTA, _mode)) { return; } @@ -215,59 +182,6 @@ contract L2Genesis is Deployer { if (writeForkGenesisAllocs(_fork, Fork.HOLOCENE, _mode)) { return; } - - if (writeForkGenesisAllocs(_fork, Fork.ISTHMUS, _mode)) { - return; - } - } - - /// @notice Sets network-specific configuration in the L1Block contract - /// @param _l1Dependencies The L1 contract dependencies needed for configuration - function _setNetworkConfig(L1Dependencies memory _l1Dependencies, DeployConfig _config) internal { - console.log("L2Genesis: Modify the standard L2 genesis with network specific configuration"); - vm.startPrank(Constants.DEPOSITOR_ACCOUNT); - - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS, abi.encode(_l1Dependencies.l1ERC721BridgeProxy) - ); - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS, abi.encode(_l1Dependencies.l1CrossDomainMessengerProxy) - ); - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS, abi.encode(_l1Dependencies.l1StandardBridgeProxy) - ); - - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.REMOTE_CHAIN_ID, abi.encode(_config.l1ChainID()) - ); - - bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: _config.sequencerFeeVaultRecipient(), - _amount: _config.sequencerFeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(_config.sequencerFeeVaultWithdrawalNetwork()) - }); - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig) - ); - - bytes32 baseFeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: _config.baseFeeVaultRecipient(), - _amount: _config.baseFeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(_config.baseFeeVaultWithdrawalNetwork()) - }); - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.BASE_FEE_VAULT_CONFIG, abi.encode(baseFeeVaultConfig) - ); - - bytes32 l1FeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: _config.l1FeeVaultRecipient(), - _amount: _config.l1FeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(_config.l1FeeVaultWithdrawalNetwork()) - }); - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).setConfig( - Types.ConfigType.L1_FEE_VAULT_CONFIG, abi.encode(l1FeeVaultConfig) - ); - vm.stopPrank(); } function writeForkGenesisAllocs(Fork _latest, Fork _current, OutputMode _mode) internal returns (bool isLatest_) { @@ -327,20 +241,24 @@ contract L2Genesis is Deployer { /// @notice Sets all the implementations for the predeploy proxies. For contracts without proxies, /// sets the deployed bytecode at their expected predeploy address. /// LEGACY_ERC20_ETH and L1_MESSAGE_SENDER are deprecated and are not set. - function setPredeployImplementations() internal { + function setPredeployImplementations(L1Dependencies memory _l1Dependencies) internal { + console.log("Setting predeploy implementations with L1 contract dependencies:"); + console.log("- L1CrossDomainMessengerProxy: %s", _l1Dependencies.l1CrossDomainMessengerProxy); + console.log("- L1StandardBridgeProxy: %s", _l1Dependencies.l1StandardBridgeProxy); + console.log("- L1ERC721BridgeProxy: %s", _l1Dependencies.l1ERC721BridgeProxy); setLegacyMessagePasser(); // 0 // 01: legacy, not used in OP-Stack setDeployerWhitelist(); // 2 // 3,4,5: legacy, not used in OP-Stack. setWETH(); // 6: WETH (not behind a proxy) - setL2CrossDomainMessenger(); // 7 + setL2CrossDomainMessenger(_l1Dependencies.l1CrossDomainMessengerProxy); // 7 // 8,9,A,B,C,D,E: legacy, not used in OP-Stack. setGasPriceOracle(); // f - setL2StandardBridge(); // 10 + setL2StandardBridge(_l1Dependencies.l1StandardBridgeProxy); // 10 setSequencerFeeVault(); // 11 setOptimismMintableERC20Factory(); // 12 setL1BlockNumber(); // 13 - setL2ERC721Bridge(); // 14 + setL2ERC721Bridge(_l1Dependencies.l1ERC721BridgeProxy); // 14 setL1Block(); // 15 setL2ToL1MessagePasser(); // 16 setOptimismMintableERC721Factory(); // 17 @@ -366,9 +284,12 @@ contract L2Genesis is Deployer { // Note the ProxyAdmin implementation itself is behind a proxy that owns itself. address impl = _setImplementationCode(Predeploys.PROXY_ADMIN); - // update the proxy to not be uninitialized (although not standard initialize pattern) bytes32 _ownerSlot = bytes32(0); - vm.store(impl, _ownerSlot, bytes32(uint256(0xdead))); + + // there is no initialize() function, so we just set the storage manually. + vm.store(Predeploys.PROXY_ADMIN, _ownerSlot, bytes32(uint256(uint160(cfg.proxyAdminOwner())))); + // update the proxy to not be uninitialized (although not standard initialize pattern) + vm.store(impl, _ownerSlot, bytes32(uint256(uint160(cfg.proxyAdminOwner())))); } function setL2ToL1MessagePasser() public { @@ -376,40 +297,102 @@ contract L2Genesis is Deployer { } /// @notice This predeploy is following the safety invariant #1. - function setL2CrossDomainMessenger() public { - _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + function setL2CrossDomainMessenger(address payable _l1CrossDomainMessengerProxy) public { + address impl = _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + + IL2CrossDomainMessenger(impl).initialize({ _l1CrossDomainMessenger: ICrossDomainMessenger(address(0)) }); + + IL2CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).initialize({ + _l1CrossDomainMessenger: ICrossDomainMessenger(_l1CrossDomainMessengerProxy) + }); } /// @notice This predeploy is following the safety invariant #1. - function setL2StandardBridge() public { + function setL2StandardBridge(address payable _l1StandardBridgeProxy) public { + address impl; if (cfg.useInterop()) { string memory cname = "L2StandardBridgeInterop"; - address impl = Predeploys.predeployToCodeNamespace(Predeploys.L2_STANDARD_BRIDGE); + impl = Predeploys.predeployToCodeNamespace(Predeploys.L2_STANDARD_BRIDGE); console.log("Setting %s implementation at: %s", cname, impl); vm.etch(impl, vm.getDeployedCode(string.concat(cname, ".sol:", cname))); } else { - _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE); + impl = _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE); } + + IL2StandardBridge(payable(impl)).initialize({ _otherBridge: IStandardBridge(payable(address(0))) }); + + IL2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).initialize({ + _otherBridge: IStandardBridge(_l1StandardBridgeProxy) + }); } /// @notice This predeploy is following the safety invariant #1. - function setL2ERC721Bridge() public { - _setImplementationCode(Predeploys.L2_ERC721_BRIDGE); + function setL2ERC721Bridge(address payable _l1ERC721BridgeProxy) public { + address impl = _setImplementationCode(Predeploys.L2_ERC721_BRIDGE); + + IL2ERC721Bridge(impl).initialize({ _l1ERC721Bridge: payable(address(0)) }); + + IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE).initialize({ _l1ERC721Bridge: payable(_l1ERC721BridgeProxy) }); } /// @notice This predeploy is following the safety invariant #2, function setSequencerFeeVault() public { - _setImplementationCode(Predeploys.SEQUENCER_FEE_WALLET); + ISequencerFeeVault vault = ISequencerFeeVault( + DeployUtils.create1( + "SequencerFeeVault", + DeployUtils.encodeConstructor( + abi.encodeCall( + ISequencerFeeVault.__constructor__, + ( + cfg.sequencerFeeVaultRecipient(), + cfg.sequencerFeeVaultMinimumWithdrawalAmount(), + Types.WithdrawalNetwork(cfg.sequencerFeeVaultWithdrawalNetwork()) + ) + ) + ) + ) + ); + + address impl = Predeploys.predeployToCodeNamespace(Predeploys.SEQUENCER_FEE_WALLET); + console.log("Setting %s implementation at: %s", "SequencerFeeVault", impl); + vm.etch(impl, address(vault).code); + + /// Reset so its not included state dump + vm.etch(address(vault), ""); + vm.resetNonce(address(vault)); } /// @notice This predeploy is following the safety invariant #1. function setOptimismMintableERC20Factory() public { - _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); + address impl = _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); + + IOptimismMintableERC20Factory(impl).initialize({ _bridge: address(0) }); + + IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).initialize({ + _bridge: Predeploys.L2_STANDARD_BRIDGE + }); } /// @notice This predeploy is following the safety invariant #2, function setOptimismMintableERC721Factory() public { - _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); + IOptimismMintableERC721Factory factory = IOptimismMintableERC721Factory( + DeployUtils.create1( + "OptimismMintableERC721Factory", + DeployUtils.encodeConstructor( + abi.encodeCall( + IOptimismMintableERC721Factory.__constructor__, (Predeploys.L2_ERC721_BRIDGE, cfg.l1ChainID()) + ) + ) + ) + ); + + address impl = Predeploys.predeployToCodeNamespace(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); + console.log("Setting %s implementation at: %s", "OptimismMintableERC721Factory", impl); + vm.etch(impl, address(factory).code); + + /// Reset so its not included state dump + vm.etch(address(factory), ""); + vm.resetNonce(address(factory)); } /// @notice This predeploy is following the safety invariant #1. @@ -456,12 +439,56 @@ contract L2Genesis is Deployer { /// @notice This predeploy is following the safety invariant #2. function setBaseFeeVault() public { - _setImplementationCode(Predeploys.BASE_FEE_VAULT); + IBaseFeeVault vault = IBaseFeeVault( + DeployUtils.create1( + "BaseFeeVault", + DeployUtils.encodeConstructor( + abi.encodeCall( + IBaseFeeVault.__constructor__, + ( + cfg.baseFeeVaultRecipient(), + cfg.baseFeeVaultMinimumWithdrawalAmount(), + Types.WithdrawalNetwork(cfg.baseFeeVaultWithdrawalNetwork()) + ) + ) + ) + ) + ); + + address impl = Predeploys.predeployToCodeNamespace(Predeploys.BASE_FEE_VAULT); + console.log("Setting %s implementation at: %s", "BaseFeeVault", impl); + vm.etch(impl, address(vault).code); + + /// Reset so its not included state dump + vm.etch(address(vault), ""); + vm.resetNonce(address(vault)); } /// @notice This predeploy is following the safety invariant #2. function setL1FeeVault() public { - _setImplementationCode(Predeploys.L1_FEE_VAULT); + IL1FeeVault vault = IL1FeeVault( + DeployUtils.create1( + "L1FeeVault", + DeployUtils.encodeConstructor( + abi.encodeCall( + IL1FeeVault.__constructor__, + ( + cfg.l1FeeVaultRecipient(), + cfg.l1FeeVaultMinimumWithdrawalAmount(), + Types.WithdrawalNetwork(cfg.l1FeeVaultWithdrawalNetwork()) + ) + ) + ) + ) + ); + + address impl = Predeploys.predeployToCodeNamespace(Predeploys.L1_FEE_VAULT); + console.log("Setting %s implementation at: %s", "L1FeeVault", impl); + vm.etch(impl, address(vault).code); + + /// Reset so its not included state dump + vm.etch(address(vault), ""); + vm.resetNonce(address(vault)); } /// @notice This predeploy is following the safety invariant #2. diff --git a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol index e176a1d7c53e..25d67be3828c 100644 --- a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol +++ b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol @@ -13,7 +13,6 @@ import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Types } from "scripts/libraries/Types.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -41,7 +40,8 @@ library ChainAssertions { function postDeployAssertions( Types.ContractSet memory _prox, DeployConfig _cfg, - uint256 _l2OutputOracleStartingTimestamp + uint256 _l2OutputOracleStartingTimestamp, + Vm _vm ) internal view @@ -52,7 +52,7 @@ library ChainAssertions { require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt))); checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); - checkL1CrossDomainMessenger({ _contracts: _prox, _isProxy: true }); + checkL1CrossDomainMessenger({ _contracts: _prox, _vm: _vm, _isProxy: true }); checkL1StandardBridge({ _contracts: _prox, _isProxy: true }); checkL2OutputOracle({ _contracts: _prox, @@ -77,7 +77,7 @@ library ChainAssertions { ); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(config), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(config), _slot: 0, _offset: 0 }); IResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig(); @@ -109,18 +109,18 @@ library ChainAssertions { require(config.optimismPortal() == _contracts.OptimismPortal, "CHECK-SCFG-200"); require(config.optimismMintableERC20Factory() == _contracts.OptimismMintableERC20Factory, "CHECK-SCFG-210"); } else { - require(config.owner() == address(0), "CHECK-SCFG-220"); + require(config.owner() == address(0xdead), "CHECK-SCFG-220"); require(config.overhead() == 0, "CHECK-SCFG-230"); - require(config.scalar() == 0, "CHECK-SCFG-240"); + require(config.scalar() == uint256(0x01) << 248, "CHECK-SCFG-240"); // version 1 require(config.basefeeScalar() == 0, "CHECK-SCFG-250"); require(config.blobbasefeeScalar() == 0, "CHECK-SCFG-260"); require(config.batcherHash() == bytes32(0), "CHECK-SCFG-270"); - require(config.gasLimit() == 0, "CHECK-SCFG-280"); + require(config.gasLimit() == 1, "CHECK-SCFG-280"); require(config.unsafeBlockSigner() == address(0), "CHECK-SCFG-290"); // Check _config - require(resourceConfig.maxResourceLimit == 0, "CHECK-SCFG-300"); - require(resourceConfig.elasticityMultiplier == 0, "CHECK-SCFG-310"); - require(resourceConfig.baseFeeMaxChangeDenominator == 0, "CHECK-SCFG-320"); + require(resourceConfig.maxResourceLimit == 1, "CHECK-SCFG-300"); + require(resourceConfig.elasticityMultiplier == 1, "CHECK-SCFG-310"); + require(resourceConfig.baseFeeMaxChangeDenominator == 2, "CHECK-SCFG-320"); require(resourceConfig.systemTxMaxGas == 0, "CHECK-SCFG-330"); require(resourceConfig.minimumBaseFee == 0, "CHECK-SCFG-340"); require(resourceConfig.maximumBaseFee == 0, "CHECK-SCFG-350"); @@ -163,7 +163,7 @@ library ChainAssertions { } /// @notice Asserts that the L1CrossDomainMessenger is setup correctly - function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, bool _isProxy) internal view { + function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, Vm _vm, bool _isProxy) internal view { IL1CrossDomainMessenger messenger = IL1CrossDomainMessenger(_contracts.L1CrossDomainMessenger); console.log( "Running chain assertions on the L1CrossDomainMessenger %s at %s", @@ -173,7 +173,7 @@ library ChainAssertions { require(address(messenger) != address(0), "CHECK-L1XDM-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "CHECK-L1XDM-20"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "CHECK-L1XDM-30"); @@ -182,6 +182,8 @@ library ChainAssertions { require(address(messenger.PORTAL()) == _contracts.OptimismPortal, "CHECK-L1XDM-40"); require(address(messenger.portal()) == _contracts.OptimismPortal, "CHECK-L1XDM-50"); require(address(messenger.superchainConfig()) == _contracts.SuperchainConfig, "CHECK-L1XDM-60"); + bytes32 xdmSenderSlot = _vm.load(address(messenger), bytes32(uint256(204))); + require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "CHECK-L1XDM-70"); } else { require(address(messenger.PORTAL()) == address(0), "CHECK-L1XDM-80"); require(address(messenger.portal()) == address(0), "CHECK-L1XDM-90"); @@ -200,7 +202,7 @@ library ChainAssertions { require(address(bridge) != address(0), "CHECK-L1SB-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); if (_isProxy) { require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger, "CHECK-L1SB-20"); @@ -235,7 +237,7 @@ library ChainAssertions { require(address(factory) != address(0), "CHECK-DG-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); // The same check is made for both proxy and implementation require(factory.owner() == _expectedOwner, "CHECK-DG-20"); @@ -275,7 +277,7 @@ library ChainAssertions { require(address(weth) != address(0), "CHECK-DWETH-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); if (_isProxy) { require(weth.owner() == _expectedOwner, "CHECK-DWETH-20"); @@ -306,7 +308,7 @@ library ChainAssertions { require(address(weth) != address(0), "CHECK-PDWETH-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); if (_isProxy) { require(weth.owner() == _expectedOwner, "CHECK-PDWETH-20"); @@ -337,7 +339,7 @@ library ChainAssertions { require(address(oracle) != address(0), "CHECK-L2OO-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(oracle), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(oracle), _slot: 0, _offset: 0 }); if (_isProxy) { require(oracle.SUBMISSION_INTERVAL() == _cfg.l2OutputOracleSubmissionInterval(), "CHECK-L2OO-20"); @@ -379,7 +381,7 @@ library ChainAssertions { require(address(factory) != address(0), "CHECK-MERC20F-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); if (_isProxy) { require(factory.BRIDGE() == _contracts.L1StandardBridge, "CHECK-MERC20F-10"); @@ -402,7 +404,7 @@ library ChainAssertions { require(address(bridge) != address(0), "CHECK-L1ERC721B-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); + assertInitializedSlotIsSet({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "CHECK-L1ERC721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "CHECK-L1ERC721B-20"); @@ -429,7 +431,7 @@ library ChainAssertions { require(address(portal) != address(0), "CHECK-OP-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); address guardian = _cfg.superchainConfigGuardian(); if (guardian.code.length == 0) { @@ -469,7 +471,7 @@ library ChainAssertions { require(address(portal) != address(0), "CHECK-OP2-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); address guardian = _cfg.superchainConfigGuardian(); if (guardian.code.length == 0) { @@ -512,7 +514,7 @@ library ChainAssertions { require(address(versions) != address(0), "CHECK-PV-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(versions), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(versions), _slot: 0, _offset: 0 }); if (_isProxy) { require(versions.owner() == _cfg.finalSystemOwner(), "CHECK-PV-20"); @@ -544,7 +546,7 @@ library ChainAssertions { require(address(superchainConfig) != address(0), "CHECK-SC-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 }); if (_isProxy) { require(superchainConfig.guardian() == _cfg.superchainConfigGuardian(), "CHECK-SC-20"); @@ -566,7 +568,7 @@ library ChainAssertions { require(address(opcm) != address(0), "CHECK-OPCM-10"); // Check that the contract is initialized - DeployUtils.assertInitialized({ _contractAddress: address(opcm), _slot: 0, _offset: 0 }); + assertInitializedSlotIsSet({ _contractAddress: address(opcm), _slot: 0, _offset: 0 }); // These values are immutable so are shared by the proxy and implementation require(address(opcm.superchainConfig()) == address(_contracts.SuperchainConfig), "CHECK-OPCM-30"); @@ -574,4 +576,15 @@ library ChainAssertions { // TODO: Add assertions for blueprints and setters? } + + /// @dev Asserts that for a given contract the value of a storage slot at an offset is 1 or 0xff. + /// A call to `initialize` will set it to 1 and a call to _disableInitializers will set it to 0xff. + function assertInitializedSlotIsSet(address _contractAddress, uint256 _slot, uint256 _offset) internal view { + bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot)); + uint8 val = uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF); + require( + val == uint8(1) || val == uint8(0xff), + "ChainAssertions: storage value is not 1 or 0xff at the given slot and offset" + ); + } } diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index ad3a53b159d5..d40c03987d53 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -155,7 +155,7 @@ contract Deploy is Deployer { DelayedWETH: getAddress("DelayedWETHProxy"), PermissionedDelayedWETH: getAddress("PermissionedDelayedWETHProxy"), AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"), - OptimismMintableERC20Factory: getAddress("L1OptimismMintableERC20FactoryProxy"), + OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"), OptimismPortal: getAddress("OptimismPortalProxy"), OptimismPortal2: getAddress("OptimismPortalProxy"), SystemConfig: getAddress("SystemConfigProxy"), @@ -176,7 +176,7 @@ contract Deploy is Deployer { DelayedWETH: getAddress("DelayedWETH"), PermissionedDelayedWETH: getAddress("PermissionedDelayedWETH"), AnchorStateRegistry: getAddress("AnchorStateRegistry"), - OptimismMintableERC20Factory: getAddress("L1OptimismMintableERC20Factory"), + OptimismMintableERC20Factory: getAddress("OptimismMintableERC20Factory"), OptimismPortal: getAddress("OptimismPortal"), OptimismPortal2: getAddress("OptimismPortal2"), SystemConfig: getAddress("SystemConfig"), @@ -398,9 +398,7 @@ contract Deploy is Deployer { } save("L1CrossDomainMessenger", address(dio.l1CrossDomainMessengerImpl())); - // Save under both names for backwards compatibility save("OptimismMintableERC20Factory", address(dio.optimismMintableERC20FactoryImpl())); - save("L1OptimismMintableERC20Factory", address(dio.optimismMintableERC20FactoryImpl())); save("SystemConfig", address(dio.systemConfigImpl())); save("L1StandardBridge", address(dio.l1StandardBridgeImpl())); save("L1ERC721Bridge", address(dio.l1ERC721BridgeImpl())); @@ -415,7 +413,7 @@ contract Deploy is Deployer { save("OPContractsManager", address(dio.opcmImpl())); Types.ContractSet memory contracts = _impls(); - ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _isProxy: false }); + ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _vm: vm, _isProxy: false }); ChainAssertions.checkL1StandardBridge({ _contracts: contracts, _isProxy: false }); ChainAssertions.checkL1ERC721Bridge({ _contracts: contracts, _isProxy: false }); ChainAssertions.checkOptimismPortal2({ _contracts: contracts, _cfg: cfg, _isProxy: false }); @@ -458,9 +456,7 @@ contract Deploy is Deployer { save("AddressManager", address(deployOutput.addressManager)); save("L1ERC721BridgeProxy", address(deployOutput.l1ERC721BridgeProxy)); save("SystemConfigProxy", address(deployOutput.systemConfigProxy)); - // Save under both names for backwards compatibility save("OptimismMintableERC20FactoryProxy", address(deployOutput.optimismMintableERC20FactoryProxy)); - save("L1OptimismMintableERC20FactoryProxy", address(deployOutput.optimismMintableERC20FactoryProxy)); save("L1StandardBridgeProxy", address(deployOutput.l1StandardBridgeProxy)); save("L1CrossDomainMessengerProxy", address(deployOutput.l1CrossDomainMessengerProxy)); @@ -827,15 +823,12 @@ contract Deploy is Deployer { _data: abi.encodeCall( ISystemConfig.initialize, ( - ISystemConfig.Roles({ - owner: cfg.finalSystemOwner(), - feeAdmin: cfg.systemConfigFeeAdmin(), - unsafeBlockSigner: cfg.p2pSequencerAddress(), - batcherHash: batcherHash - }), + cfg.finalSystemOwner(), cfg.basefeeScalar(), cfg.blobbasefeeScalar(), + batcherHash, uint64(cfg.l2GenesisBlockGasLimit()), + cfg.p2pSequencerAddress(), Constants.DEFAULT_RESOURCE_CONFIG(), cfg.batchInboxAddress(), ISystemConfig.Addresses({ @@ -844,7 +837,7 @@ contract Deploy is Deployer { l1StandardBridge: mustGetAddress("L1StandardBridgeProxy"), disputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"), optimismPortal: mustGetAddress("OptimismPortalProxy"), - optimismMintableERC20Factory: mustGetAddress("L1OptimismMintableERC20FactoryProxy"), + optimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"), gasPayingToken: customGasTokenAddress }) ) @@ -1271,8 +1264,7 @@ contract Deploy is Deployer { batcher: cfg.batchSenderAddress(), unsafeBlockSigner: cfg.p2pSequencerAddress(), proposer: cfg.l2OutputOracleProposer(), - challenger: cfg.l2OutputOracleChallenger(), - systemConfigFeeAdmin: cfg.systemConfigFeeAdmin() + challenger: cfg.l2OutputOracleChallenger() }), basefeeScalar: cfg.basefeeScalar(), blobBasefeeScalar: cfg.blobbasefeeScalar(), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index a95346f093b5..69341dd3e874 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -86,7 +86,6 @@ contract DeployConfig is Script { uint256 public daResolveWindow; uint256 public daBondSize; uint256 public daResolverRefundPercentage; - address public systemConfigFeeAdmin; bool public useCustomGasToken; address public customGasTokenAddress; @@ -102,7 +101,6 @@ contract DeployConfig is Script { } finalSystemOwner = stdJson.readAddress(_json, "$.finalSystemOwner"); - systemConfigFeeAdmin = stdJson.readAddress(_json, "$.systemConfigFeeAdmin"); superchainConfigGuardian = stdJson.readAddress(_json, "$.superchainConfigGuardian"); l1ChainID = stdJson.readUint(_json, "$.l1ChainID"); l2ChainID = stdJson.readUint(_json, "$.l2ChainID"); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 40f9b79c5580..c9048a07dfa3 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -29,8 +29,7 @@ import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMessenger.sol"; import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; -import { IL1OptimismMintableERC20Factory as IOptimismMintableERC20Factory } from - "src/L1/interfaces/IL1OptimismMintableERC20Factory.sol"; +import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; import { OPContractsManagerInterop } from "src/L1/OPContractsManagerInterop.sol"; import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; @@ -371,19 +370,19 @@ contract DeployImplementationsOutput is BaseDeployIO { DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 }); - require(systemConfig.owner() == address(0), "SYSCON-10"); + require(systemConfig.owner() == address(0xdead), "SYSCON-10"); require(systemConfig.overhead() == 0, "SYSCON-20"); - require(systemConfig.scalar() == 0, "SYSCON-30"); + require(systemConfig.scalar() == uint256(0x01) << 248, "SYSCON-30"); require(systemConfig.basefeeScalar() == 0, "SYSCON-40"); require(systemConfig.blobbasefeeScalar() == 0, "SYSCON-50"); require(systemConfig.batcherHash() == bytes32(0), "SYSCON-60"); - require(systemConfig.gasLimit() == 0, "SYSCON-70"); + require(systemConfig.gasLimit() == 1, "SYSCON-70"); require(systemConfig.unsafeBlockSigner() == address(0), "SYSCON-80"); IResourceMetering.ResourceConfig memory resourceConfig = systemConfig.resourceConfig(); - require(resourceConfig.maxResourceLimit == 0, "SYSCON-90"); - require(resourceConfig.elasticityMultiplier == 0, "SYSCON-100"); - require(resourceConfig.baseFeeMaxChangeDenominator == 0, "SYSCON-110"); + require(resourceConfig.maxResourceLimit == 1, "SYSCON-90"); + require(resourceConfig.elasticityMultiplier == 1, "SYSCON-100"); + require(resourceConfig.baseFeeMaxChangeDenominator == 2, "SYSCON-110"); require(resourceConfig.systemTxMaxGas == 0, "SYSCON-120"); require(resourceConfig.minimumBaseFee == 0, "SYSCON-130"); require(resourceConfig.maximumBaseFee == 0, "SYSCON-140"); @@ -401,7 +400,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidL1CrossDomainMessengerImpl(DeployImplementationsInput) internal view { IL1CrossDomainMessenger messenger = l1CrossDomainMessengerImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-10"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-20"); @@ -409,15 +408,14 @@ contract DeployImplementationsOutput is BaseDeployIO { require(address(messenger.portal()) == address(0), "L1xDM-40"); require(address(messenger.superchainConfig()) == address(0), "L1xDM-50"); - // TODO: vm.expectRevert is not supported by op-chain-ops, so we can't check this yet. - // vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); - // messenger.xDomainMessageSender(); + bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204))); + require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60"); } function assertValidL1ERC721BridgeImpl(DeployImplementationsInput) internal view { IL1ERC721Bridge bridge = l1ERC721BridgeImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20"); @@ -429,7 +427,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidL1StandardBridgeImpl(DeployImplementationsInput) internal view { IL1StandardBridge bridge = l1StandardBridgeImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); require(address(bridge.MESSENGER()) == address(0), "L1SB-10"); require(address(bridge.messenger()) == address(0), "L1SB-20"); @@ -441,7 +439,7 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidOptimismMintableERC20FactoryImpl(DeployImplementationsInput) internal view { IOptimismMintableERC20Factory factory = optimismMintableERC20FactoryImpl(); - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); require(address(factory.BRIDGE()) == address(0), "MERC20F-10"); require(address(factory.bridge()) == address(0), "MERC20F-20"); @@ -790,7 +788,7 @@ contract DeployImplementations is Script { vm.broadcast(msg.sender); impl = IOptimismMintableERC20Factory( DeployUtils.create1({ - _name: "L1OptimismMintableERC20Factory", + _name: "OptimismMintableERC20Factory", _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismMintableERC20Factory.__constructor__, ())) }) ); @@ -798,7 +796,7 @@ contract DeployImplementations is Script { revert(string.concat("DeployImplementations: failed to deploy release ", release)); } - vm.label(address(impl), "L1OptimismMintableERC20FactoryImpl"); + vm.label(address(impl), "OptimismMintableERC20FactoryImpl"); _dio.set(_dio.optimismMintableERC20FactoryImpl.selector, address(impl)); } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 247a0575f009..7a5d1f5cc6b2 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -38,7 +38,6 @@ import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimis contract DeployOPChainInput is BaseDeployIO { address internal _opChainProxyAdminOwner; address internal _systemConfigOwner; - address internal _systemConfigFeeAdmin; address internal _batcher; address internal _unsafeBlockSigner; address internal _proposer; @@ -65,7 +64,6 @@ contract DeployOPChainInput is BaseDeployIO { require(_addr != address(0), "DeployOPChainInput: cannot set zero address"); if (_sel == this.opChainProxyAdminOwner.selector) _opChainProxyAdminOwner = _addr; else if (_sel == this.systemConfigOwner.selector) _systemConfigOwner = _addr; - else if (_sel == this.systemConfigFeeAdmin.selector) _systemConfigFeeAdmin = _addr; else if (_sel == this.batcher.selector) _batcher = _addr; else if (_sel == this.unsafeBlockSigner.selector) _unsafeBlockSigner = _addr; else if (_sel == this.proposer.selector) _proposer = _addr; @@ -125,11 +123,6 @@ contract DeployOPChainInput is BaseDeployIO { return _systemConfigOwner; } - function systemConfigFeeAdmin() public view returns (address) { - require(_systemConfigFeeAdmin != address(0), "DeployOPChainInput: not set"); - return _systemConfigFeeAdmin; - } - function batcher() public view returns (address) { require(_batcher != address(0), "DeployOPChainInput: not set"); return _batcher; @@ -362,8 +355,7 @@ contract DeployOPChain is Script { batcher: _doi.batcher(), unsafeBlockSigner: _doi.unsafeBlockSigner(), proposer: _doi.proposer(), - challenger: _doi.challenger(), - systemConfigFeeAdmin: _doi.systemConfigFeeAdmin() + challenger: _doi.challenger() }); OPContractsManager.DeployInput memory deployInput = OPContractsManager.DeployInput({ roles: roles, @@ -580,7 +572,7 @@ contract DeployOPChain is Script { function assertValidL1CrossDomainMessenger(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1CrossDomainMessenger messenger = _doo.l1CrossDomainMessengerProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 250, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-10"); require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-20"); @@ -589,16 +581,15 @@ contract DeployOPChain is Script { require(address(messenger.portal()) == address(_doo.optimismPortalProxy()), "L1xDM-40"); require(address(messenger.superchainConfig()) == address(_doi.opcmProxy().superchainConfig()), "L1xDM-50"); - // TODO: vm.expectRevert is not supported by op-chain-ops, so we can't check this yet. - // vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); - // messenger.xDomainMessageSender(); + bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204))); + require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60"); } function assertValidL1StandardBridge(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1StandardBridge bridge = _doo.l1StandardBridgeProxy(); IL1CrossDomainMessenger messenger = _doo.l1CrossDomainMessengerProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 49, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); require(address(bridge.MESSENGER()) == address(messenger), "L1SB-10"); require(address(bridge.messenger()) == address(messenger), "L1SB-20"); @@ -610,7 +601,7 @@ contract DeployOPChain is Script { function assertValidOptimismMintableERC20Factory(DeployOPChainInput, DeployOPChainOutput _doo) internal { IOptimismMintableERC20Factory factory = _doo.optimismMintableERC20FactoryProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 51, _offset: 0 }); + DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); require(factory.BRIDGE() == address(_doo.l1StandardBridgeProxy()), "MERC20F-10"); require(factory.bridge() == address(_doo.l1StandardBridgeProxy()), "MERC20F-20"); @@ -619,7 +610,7 @@ contract DeployOPChain is Script { function assertValidL1ERC721Bridge(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IL1ERC721Bridge bridge = _doo.l1ERC721BridgeProxy(); - DeployUtils.assertInitialized({ _contractAddress: address(bridge) }); + DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20"); diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index f2f39e660acd..74492556e1b1 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -359,7 +359,6 @@ contract DeploySuperchain is Script { function deployAndInitializeSuperchainConfig(DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public { address guardian = _dsi.guardian(); - address upgrader = _dsi.superchainProxyAdminOwner(); bool paused = _dsi.paused(); IProxyAdmin superchainProxyAdmin = _dso.superchainProxyAdmin(); @@ -377,7 +376,7 @@ contract DeploySuperchain is Script { superchainProxyAdmin.upgradeAndCall( payable(address(superchainConfigProxy)), address(superchainConfigImpl), - abi.encodeCall(ISuperchainConfig.initialize, (guardian, upgrader, paused)) + abi.encodeCall(ISuperchainConfig.initialize, (guardian, paused)) ); vm.stopBroadcast(); diff --git a/packages/contracts-bedrock/scripts/libraries/Config.sol b/packages/contracts-bedrock/scripts/libraries/Config.sol index 4aca8bc31e3c..18084761e807 100644 --- a/packages/contracts-bedrock/scripts/libraries/Config.sol +++ b/packages/contracts-bedrock/scripts/libraries/Config.sol @@ -34,11 +34,10 @@ enum Fork { ECOTONE, FJORD, GRANITE, - HOLOCENE, - ISTHMUS + HOLOCENE } -Fork constant LATEST_FORK = Fork.ISTHMUS; +Fork constant LATEST_FORK = Fork.HOLOCENE; library ForkUtils { function toString(Fork _fork) internal pure returns (string memory) { @@ -54,8 +53,6 @@ library ForkUtils { return "granite"; } else if (_fork == Fork.HOLOCENE) { return "holocene"; - } else if (_fork == Fork.ISTHMUS) { - return "isthmus"; } else { return "unknown"; } @@ -171,8 +168,6 @@ library Config { return Fork.GRANITE; } else if (forkHash == keccak256(bytes("holocene"))) { return Fork.HOLOCENE; - } else if (forkHash == keccak256(bytes("isthmus"))) { - return Fork.ISTHMUS; } else { revert(string.concat("Config: unknown fork: ", forkStr)); } diff --git a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol index 4197746ca436..da9aea12a0ac 100644 --- a/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol +++ b/packages/contracts-bedrock/scripts/libraries/DeployUtils.sol @@ -351,11 +351,6 @@ library DeployUtils { } } - /// @notice The unstructured storage slot that is used for v5 openzeppelin initializable. Computed as: - /// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & - /// ~bytes32(uint256(0xff)) - bytes32 internal constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; - /// @notice Asserts that for a given contract the value of a storage slot at an offset is 1 or /// `type(uint8).max`. The value is set to 1 when a contract is initialized, and set to /// `type(uint8).max` when `_disableInitializers` is called. @@ -367,9 +362,4 @@ library DeployUtils { "DeployUtils: value at the given slot and offset does not indicate initialization" ); } - - /// @notice Asserts that the contract has been initialized, assuming that it is using openzeppelin v5 initializable. - function assertInitialized(address _contractAddress) internal view { - assertInitialized({ _contractAddress: _contractAddress, _slot: uint256(INITIALIZABLE_STORAGE), _offset: 0 }); - } } diff --git a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol index a646a7b6d02f..944206694d78 100644 --- a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol +++ b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol @@ -5,7 +5,6 @@ import { Vm } from "forge-std/Vm.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { LibString } from "@solady/utils/LibString.sol"; import { Executables } from "scripts/libraries/Executables.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Process } from "scripts/libraries/Process.sol"; /// @notice Contains information about a storage slot. Mirrors the layout of the storage @@ -191,19 +190,6 @@ library ForgeArtifacts { function getInitializedSlot(string memory _contractName) internal returns (StorageSlot memory slot_) { string memory storageLayout = getStorageLayout(_contractName); - // Contracts that use oz v5 should be here - if (LibString.eq(_contractName, "L1ERC721Bridge")) { - StorageSlot memory slot = StorageSlot({ - astId: 0, - _contract: _contractName, - label: "_initialized", - offset: 0, - slot: vm.toString(DeployUtils.INITIALIZABLE_STORAGE), - _type: "bool" - }); - return slot; - } - // FaultDisputeGame and PermissionedDisputeGame use a different name for the initialized storage slot. string memory slotName = "_initialized"; string memory slotType = "t_uint8"; @@ -228,10 +214,7 @@ library ForgeArtifacts { slotType, "\")'" ); - - bytes memory result = Process.run(command); - require(result.length > 0, string.concat("ForgeArtifacts: ", _contractName, "is not initializable")); - bytes memory rawSlot = vm.parseJson(string(result)); + bytes memory rawSlot = vm.parseJson(string(Process.run(command))); slot_ = abi.decode(rawSlot, (StorageSlot)); } diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 0af4722ac9da..1e14f5d286b5 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -8,60 +8,56 @@ "sourceCodeHash": "0x30e83a535ef27b2e900c831c4e1a4ec2750195350011c4fdacda1da9db2d167b" }, "src/L1/L1CrossDomainMessenger.sol": { - "initCodeHash": "0x8d0351b1c13be9e53ba9d3c6174e0ef0ae5c37cfdf31da446533548f5c56553a", - "sourceCodeHash": "0x5b00fc7b2450c008cefb283779237f7402722182eb9e5210439d5a01359dc38f" + "initCodeHash": "0x2e9cb3ceb5e55341b311f0666ef7655df4fafae75afdfbcd701cd9c9b2b017d5", + "sourceCodeHash": "0x848ec3774be17bcc8ba65a23d08e35e979b3f39f9d2ac8a810188f945c69c9ea" }, "src/L1/L1ERC721Bridge.sol": { - "initCodeHash": "0x19542aad97e146219d4d3cdcfb29f8f1591815731c51216e8424e754556a21a0", - "sourceCodeHash": "0x71fc58927450aa7f6e97688e7a5ecbfadfa235a26523d4c540183647ccbf7fdf" - }, - "src/L1/L1OptimismMintableERC20Factory.sol": { - "initCodeHash": "0xc6b0a84d485c8503bd1ce077fbfa5533ad1f6a001feab31d9c21948d02d47827", - "sourceCodeHash": "0x9432cd22e23347f4b397706b4eb28ee4eaba481f4914d2d685d77c413963326d" + "initCodeHash": "0xb3bf093ea83a24574a6093bebf5b2aea707355ed8d6702b2b5eb292e75b6ae42", + "sourceCodeHash": "0x289de9f40898b6305deecc6b60cdf566aa6c6a1444f713c3a0af23ea7878207e" }, "src/L1/L1StandardBridge.sol": { - "initCodeHash": "0x0a17e22cb56c915bf1aaf3a61ac28de2dca4f2122ba57fe597a3dd612e245478", - "sourceCodeHash": "0xa706c8d55d1e34a86d42a113cd486ccb809a36ff956cf32fbcb839ca2cdfbf0b" + "initCodeHash": "0x802f72745bb9a82dc049377bb9cf6b58f35aec388aeb957b28a5e14f28d91bc1", + "sourceCodeHash": "0x24b784645b065a5393a2115a078d67f91eb09afd5e70baf81daf975381f16155" }, "src/L1/L2OutputOracle.sol": { "initCodeHash": "0x1182bfb87c4ab399b912ca7fe18cdbf4b24c414e078fb0a55bd3c44d442d3ed1", "sourceCodeHash": "0x4132ff37d267cb12224b75ea806c0aa7d25407b0d66ce526d7fcda8f7d223882" }, "src/L1/OPContractsManager.sol": { - "initCodeHash": "0xa6e3fda99d87e69c3a2d2b1efb2f419cae75b1f2a0295548f5f2db84432dd9c4", - "sourceCodeHash": "0x647ec3500bca93169dd5384919c6787238da40ffd53fc5d1c3b7c61701a9d398" + "initCodeHash": "0xd58cb3978affc5c1457cdd498ff8420c90aef804d4c3b62cf42ab2691986d6d2", + "sourceCodeHash": "0x7bfa6eff76176649fe600303cd60009a0f6e282cbaec55836b5ea1f8875cbeb5" }, "src/L1/OptimismPortal.sol": { - "initCodeHash": "0x25c4212bac52f172b193cb3c36376074f239491d53c0d83c5081df7042cd02f8", - "sourceCodeHash": "0x6300532cb22fd9848bf54891c3a48003c7108470199a200d082b740c91e0a017" + "initCodeHash": "0x152167cfa18635ae4918a6eb3371a599cfa084418c0a652799cdb48bfc0ee0cc", + "sourceCodeHash": "0xbe34b82900d02f71bb0949818eabe49531f7e0d8d8bae01f6dac4a296530d1aa" }, "src/L1/OptimismPortal2.sol": { - "initCodeHash": "0x8e6c808992ea90f28feb12338309ff512876163ed4409a1dca6184475ab4a136", - "sourceCodeHash": "0xedc1819877c3263c43a27c5077363691ce0839fa8e61b8d000a0ba9998bf1905" + "initCodeHash": "0x218358b48f640b3fcb2d239f00dc1cd3b11517ad46c8e1efa44953d38da63540", + "sourceCodeHash": "0x66ac1212760db53a2bb1839e4cd17dc071d9273b8e6fb80646b79e91b3371c1a" }, "src/L1/OptimismPortalInterop.sol": { - "initCodeHash": "0xcda63947d6fb33478b7bdc6f20a983061b3ff9d77a7c64a6b7141af08b79fd60", - "sourceCodeHash": "0x3731c4ac1c5784b92934d3f84d8eba70321b4c6b379a9349aa4c620da6c5a7d8" + "initCodeHash": "0x39f66ac74341ec235fbdd0d79546283210bd8ac35a2ab2c4bd36c9722ce18411", + "sourceCodeHash": "0xbb98144285b9530e336f957d10b20363b350876597e30fd34821940896a2bae8" }, "src/L1/ProtocolVersions.sol": { "initCodeHash": "0xefd4806e8737716d5d2022ca2e9e9fba0a0cb5714b026166b58e472222c7d15f", "sourceCodeHash": "0x15205131bf420aa6d03c558bb75dd49cd7439caed7ccdcbfd89c4170a48c94f5" }, "src/L1/SuperchainConfig.sol": { - "initCodeHash": "0x37b2cabf25e5717ec9961bdb084b3a35ffd403c867361b106ea5e47345bc7ba9", - "sourceCodeHash": "0xbd02a2297b9088c29070bf910ec306dd31d8d185007afaaf7743435493b0ede6" + "initCodeHash": "0xfca12d9016c746e5c275b186e0ca40cfd65cf45a5665aab7589a669fea3abb47", + "sourceCodeHash": "0x39489a85bc3a5c8560f82d41b31bf7fe22f5b648f4ed538f61695a73092ea9eb" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0xc2961b2de5e12fc7f8079bb462ee9ca605940ab6072b531b5fa173dd3d3af407", - "sourceCodeHash": "0xa699d0715f3016f3acb3e3971d8560c280340a0c87e8a0a07ab80160e612c196" + "initCodeHash": "0x429058f75d97fa7a7d0166b59830909bc722324feefc40f2b41419d6335d3f37", + "sourceCodeHash": "0x5ca776041a4ddc0d28ec55db7012d669481cd4601b0e71dbd3493a67b8a7e5a5" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x2f5b58ef3db81ee004e69069d231de7c108920105edafad5e0fcf8a897201d5d", - "sourceCodeHash": "0x5c996568f655a7467e9de456b39eed7dbfde46d53a609f56b7369940fef0e1ea" + "initCodeHash": "0x277a61dcabed81a15739a8e9ed50615252bcc687cebea852e00191d0a1fbe11f", + "sourceCodeHash": "0x38361a4f70a19e1b7819e933932a0c9fd2bcebaaebcbc7942f5c00dfaa2c28df" }, "src/L2/BaseFeeVault.sol": { - "initCodeHash": "0x2eded104b7c98ea055dbd02a19ed967f5341ba169056425522ef2b3ce4b3300e", - "sourceCodeHash": "0x8428d0a173d3d58327aebc5585f1a62e3929ed56324a224e15b88bd1bca31348" + "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", + "sourceCodeHash": "0x983e8e248c61e362ba6a01dd2e217a535c9bb828dc0b4421f5f27e0577f2e14c" }, "src/L2/CrossL2Inbox.sol": { "initCodeHash": "0x31ecaebf368ab3333e80c6dc004b3c9f9a31f813c3138ab388bb3eead9f1b4ee", @@ -72,68 +68,60 @@ "sourceCodeHash": "0x0b6afdc52d1ae88d9e4bbb5dc00920e7a6bd1e9d6595bfdbae64874190f39df0" }, "src/L2/GasPriceOracle.sol": { - "initCodeHash": "0x83d50e3b34cd1b4de32f1cced28796b07aefc526cc17ceb1903ad55f4abc90b7", - "sourceCodeHash": "0x64109614b3813b0f6003bcfada1bdbdba733c623aa7b935b9e51753f1c60ed6a" + "initCodeHash": "0x7e8c2b42e10187ad649c0bf70c5688c2a4af3c412bacaec87d63c3f93ae4cfef", + "sourceCodeHash": "0xa12ce15ded3cca681b2fc9facaebbb45d740dd6f9c9496333c1c46689c9a2d99" }, "src/L2/L1Block.sol": { - "initCodeHash": "0xdd942ad49044ca92b46f39f5035995628c2c8b10c1060c0a59b65cdc20176832", - "sourceCodeHash": "0x4c6be03e3eddc17810a65e475c1c7dc1e5a15fe4c1c6d9b8ab17b3323d06e405" + "initCodeHash": "0xa919d2aa76a7ecdfd076e2b1dbece499cc85706075f16eb6fa7b1a0fa7b38c1b", + "sourceCodeHash": "0x692cfcbc06dba6328f6e5c6b500741df04e4bdf730b2069aeb5d168355ea7b6f" }, "src/L2/L1BlockInterop.sol": { - "initCodeHash": "0xfab767c50aff6bd9d232605c7016afaee34e36647142ea952fea57c98b860c2e", - "sourceCodeHash": "0xc220f197eb6aec98554fc72f68f95578a20f201a4780cada28832d21ae4d816b" + "initCodeHash": "0x62e9cc59daaf72066ac20597a666db33e9a7b3f7be71a3d47ea4841a9aca9d07", + "sourceCodeHash": "0xe57627347366d74029a0d24f0b45d7b9cf82b81c94681d0f633d5e5c37c8de4a" }, "src/L2/L1FeeVault.sol": { - "initCodeHash": "0xffd78f3c10c018ec77c3ede7599f76ad23433cc1f18794e47eb73ed5f3460dda", - "sourceCodeHash": "0x52cec09ff02282b9e7adef9f770cbc1f31682d41aece1749ba7b4ff14528eb1b" + "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", + "sourceCodeHash": "0xc7cda130f2bb3648e04d5a480082aa1789e16456c1280954d822b05d30100b2d" }, "src/L2/L2CrossDomainMessenger.sol": { - "initCodeHash": "0x9d478585dbebd5bf03e4d7a735bc5f64006e2ff99d62fc08c2202c5483005d5a", - "sourceCodeHash": "0x0726b306b41eaf326e19f0d6d1c8a9dbdc8efb2baa449e8fca5954a7b31f79c7" + "initCodeHash": "0xc496495496b96ea0eaf417c5e56b295836c12db3e6aafe2e607563e7a50b5b65", + "sourceCodeHash": "0x56edf0f36366326a92722ae3c7502bce3d80b2ee5e354181dc09ba801437a488" }, "src/L2/L2ERC721Bridge.sol": { - "initCodeHash": "0xc83703c9b18f7e4dcdbaf327894e36bf1edfbd39f9e55c5546f2f2deedf7689f", - "sourceCodeHash": "0x69e4394ac1cabc4c2cbf64988fc234f47c0750af720875ed2dbd339b752f6b87" - }, - "src/L2/L2OptimismMintableERC20Factory.sol": { - "initCodeHash": "0xb4411155cd6bf9ce710cbce80aaf7a5c8d4b509cfb3b6eca1ff1bcb9eee76013", - "sourceCodeHash": "0xb2330d5ef6c7eecc14745d5a2122daa105f2630f811548c203e5bca4a91787ec" + "initCodeHash": "0xaed0528e8b81817a0c3b41513c02e7fd678f58e34b98f02ea33d5a770a064c2f", + "sourceCodeHash": "0xf8569c75b801f38f8a5a41e94e90f159ddc5f5412804b26e3e564755a50631b8" }, "src/L2/L2StandardBridge.sol": { - "initCodeHash": "0xf6a2658d9fbda6959cff4af0ae3891534fdfdc2685c6f7210bb124ba6553d5a3", - "sourceCodeHash": "0x909e93976c2d9b419a52ce131d86aad5de9c0e8fe6bf1d5afc7257c1d9524871" + "initCodeHash": "0xcb4aa19f0cd43a35cb5c65f26c3cfd7c41f1d1e5bcc15aef6096d385df7272c9", + "sourceCodeHash": "0x89771b53b7f6e64d943afb2a4bf15395efcf20d5302b76a18e52fa7cce8cdc56" }, "src/L2/L2StandardBridgeInterop.sol": { - "initCodeHash": "0x26c04ae665855e91fc12e995bc9436b63415068da90d694f4a9f070db80027b2", - "sourceCodeHash": "0x0ca6d8900a02c213acca44c8cb56df816fa1bebd6903a9ea6d4c21525c936c12" + "initCodeHash": "0xc4eaece28d2cfca3c51247c3cce320a167a83c7fd13aea5736549d2b25e0b139", + "sourceCodeHash": "0x9e80044adf5f83c30b520ee153b75be5a152081c9e1271e7e618ecfccd1fb4ac" }, "src/L2/L2ToL1MessagePasser.sol": { "initCodeHash": "0x13fe3729beb9ed966c97bef09acb9fe5043fe651d453145073d05f2567fa988d", "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x8db6e86984e2501a4281765a9eaeafc0fe1d15c7bb802bbc15d5221a907be526", - "sourceCodeHash": "0x00e6d19b377aad27dfc8bf759dbc052fcc708d17e2a56453988ccc97e838399d" - }, - "src/L2/OptimismMintableERC721Factory.sol": { - "initCodeHash": "0xc66a3211c520beb0e3da3ce78c0f307bf4397365f147e45d31650d15678e42b2", - "sourceCodeHash": "0x35b725198c3780182408ccc76dc877b29659508592befde437754c69af7d7b5f" + "initCodeHash": "0x2a1a1ee4f47175ce661ee8e4e50cfa879b082dcb5278b1d66ddda00ed77bb744", + "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x529f3756a365a8f225d7debdd15c474c1338c8973bb65a1aad98f85753f74722", - "sourceCodeHash": "0xf68baaee0a09ea51d5a4e821df79976c0914369ebc8e5fd27bbbf89072254fc8" + "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9", + "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { - "initCodeHash": "0xb032e99f5c205c8b474da89887e350277cdd05b99ee28374b97bfae18ef7a72c", - "sourceCodeHash": "0xe8753177e8491152eb3ecbed42d5232ae545091a116f23d104a4f9621711fbda" + "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", + "sourceCodeHash": "0xf4379e49665823c877f5732f35068435ce06e2394fce6910a5e113d16cdc9f95" }, "src/L2/OptimismSuperchainERC20Factory.sol": { - "initCodeHash": "0xe6a098346699c3df248050fc3301197661d391f8e4849a93f00afeac17cae317", - "sourceCodeHash": "0x1e8380bdce27292f805ddc85fec0db5bc60fbe3fd9e0bcbd146c103062ed459a" + "initCodeHash": "0x18a362c57f08b611db98dfde96121385e938f995c84e3547c1c03fd49f9db2fd", + "sourceCodeHash": "0x450cd89d0aae7bbc85ff57a14a6d3468c24c6743f25943f6d895d34b1456c456" }, "src/L2/SequencerFeeVault.sol": { - "initCodeHash": "0x4f0017a0b906cb949ebb5931cebe00130109a4e9bb453d77b470982de5b996fb", - "sourceCodeHash": "0x8d71ff1b7d5c758ff15137c0ab1411e4abe8d91bc4fab28c981a767f6323b426" + "initCodeHash": "0xcaadbf08057b5d47f7704257e9385a29e42a7a08c818646d109c5952d3d35218", + "sourceCodeHash": "0x05bbc6039e5a9ff38987e7b9b89c69e2ee8aa4b7ca20dd002ea1bbd3d70f27f3" }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", @@ -144,12 +132,12 @@ "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0xb74ce648b49e3b7885a3252ff32aeb71e8cb515558f516c0dd61f1cf22028edc", - "sourceCodeHash": "0x095de6eeb2cd06937b3fe014392048bc494cb5ca7a4a5fd5519141a80eae3a76" + "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97", + "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359" }, "src/L2/WETH.sol": { - "initCodeHash": "0x480d4f8dbec1b0d3211bccbbdfb69796f3e90c784f724b1bbfd4703b0aafdeba", - "sourceCodeHash": "0x59a210accdd484cf00e63042cde4687e1c87ef9dbafa5fe2e081dee8e6563621" + "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", + "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, "src/cannon/MIPS.sol": { "initCodeHash": "0xa3cbf121bad13c00227ea4fef128853d9a86b7ec9158de894f99b58d38d7630a", @@ -227,9 +215,17 @@ "initCodeHash": "0x9cd677275b175812f1d5f90a127dbf7b3592714fd842a7a0de3988d716ca3eac", "sourceCodeHash": "0x5611d8082f68af566554d7f09640b4b1f0e3efee4da1372b68fc7fc538a35ac7" }, + "src/universal/OptimismMintableERC20Factory.sol": { + "initCodeHash": "0x03ad07bd7f89a29f1850fa8b5d377daf0e1d5aef6cb458a127df520549e8e8e6", + "sourceCodeHash": "0xdb6ec93782a4a217475195507740794a4f5553b9032e7ba31dc48b81f579a940" + }, "src/universal/OptimismMintableERC721.sol": { - "initCodeHash": "0x406ff004414b60852afe6ee6783ac03157ebcbe7e4787740a94576594fdfe912", - "sourceCodeHash": "0x8b4292d7556907b597d9fd3ccd5bcd8b8d61bf8e32c5a7a33d4de2557414c830" + "initCodeHash": "0x8aa309f2676d5267b6c9e411f88dc6e4badce414b8d66b330df3f60e9836380e", + "sourceCodeHash": "0x03bf7ad4d2b751bdead9930fc8f89b8e55d40dd4b2f5670fd339e87ae81f8b49" + }, + "src/universal/OptimismMintableERC721Factory.sol": { + "initCodeHash": "0x5ea977ba35558c3b75bebe28900548c763d205e40d6cf7660292b8e96bf3aea8", + "sourceCodeHash": "0x063ca3a0a2e3c592173af6157e383b5aaeff752000f98648a5c71260bb26590a" }, "src/universal/StorageSetter.sol": { "initCodeHash": "0x21b3059e9b13b330f76d02b61f61dcfa3abf3517a0b56afa0895c4b8291740bf", diff --git a/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json b/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json index 80ccfb99162a..b745bcb8184c 100644 --- a/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/BaseFeeVault.json @@ -1,4 +1,25 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_minWithdrawalAmount", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "_withdrawalNetwork", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "stateMutability": "payable", "type": "receive" @@ -35,30 +56,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "config", - "outputs": [ - { - "internalType": "address", - "name": "recipient_", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount_", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "", "type": "uint8" } ], @@ -130,7 +128,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "network_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json new file mode 100644 index 000000000000..0637a088a01e --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer0.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json new file mode 100644 index 000000000000..0637a088a01e --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/CrossDomainMessengerLegacySpacer1.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1Block.json b/packages/contracts-bedrock/snapshots/abi/L1Block.json index d477c46ddad5..020c9e942c75 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1Block.json +++ b/packages/contracts-bedrock/snapshots/abi/L1Block.json @@ -121,25 +121,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum Types.ConfigType", - "name": "_type", - "type": "uint8" - } - ], - "name": "getConfig", - "outputs": [ - { - "internalType": "bytes", - "name": "data_", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "hash", @@ -166,19 +147,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "isIsthmus", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "l1FeeOverhead", @@ -231,24 +199,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum Types.ConfigType", - "name": "_type", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "_value", - "type": "bytes" - } - ], - "name": "setConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -277,13 +227,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "setIsthmus", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -400,10 +343,5 @@ "inputs": [], "name": "NotDepositor", "type": "error" - }, - { - "inputs": [], - "name": "UnsafeCast", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json index 35ce78e5f5fd..ab089f0cec55 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json @@ -141,25 +141,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum Types.ConfigType", - "name": "_type", - "type": "uint8" - } - ], - "name": "getConfig", - "outputs": [ - { - "internalType": "bytes", - "name": "data_", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "hash", @@ -218,19 +199,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "isIsthmus", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "l1FeeOverhead", @@ -286,7 +254,7 @@ { "inputs": [ { - "internalType": "enum Types.ConfigType", + "internalType": "enum ConfigType", "name": "_type", "type": "uint8" }, @@ -329,13 +297,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "setIsthmus", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -510,10 +471,5 @@ "inputs": [], "name": "NotDepositor", "type": "error" - }, - { - "inputs": [], - "name": "UnsafeCast", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json index fb4798a3d662..22a4353cc656 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L1CrossDomainMessenger.json @@ -223,7 +223,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json b/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json index 0c6ccbd23684..33cd5b0a850b 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L1ERC721Bridge.json @@ -209,7 +209,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -342,22 +342,12 @@ "inputs": [ { "indexed": false, - "internalType": "uint64", + "internalType": "uint8", "name": "version", - "type": "uint64" + "type": "uint8" } ], "name": "Initialized", "type": "event" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json b/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json index 80ccfb99162a..b745bcb8184c 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/L1FeeVault.json @@ -1,4 +1,25 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_minWithdrawalAmount", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "_withdrawalNetwork", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "stateMutability": "payable", "type": "receive" @@ -35,30 +56,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "config", - "outputs": [ - { - "internalType": "address", - "name": "recipient_", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount_", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "", "type": "uint8" } ], @@ -130,7 +128,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "network_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json b/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json index d79fbe616a57..45480499fe9f 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L1StandardBridge.json @@ -26,7 +26,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } @@ -447,7 +447,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -468,12 +468,12 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json index c9959ad9e49f..717bbb6eb6f4 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2CrossDomainMessenger.json @@ -1,4 +1,9 @@ [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [], "name": "MESSAGE_VERSION", @@ -159,6 +164,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract CrossDomainMessenger", + "name": "_l1CrossDomainMessenger", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "l1CrossDomainMessenger", @@ -330,6 +348,19 @@ "name": "FailedRelayedMessage", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json b/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json index 8b3af6fcc3eb..e9578ad9726c 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ERC721Bridge.json @@ -1,4 +1,9 @@ [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [], "name": "MESSENGER", @@ -134,6 +139,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_l1ERC721Bridge", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "messenger", @@ -144,7 +162,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -271,5 +289,18 @@ ], "name": "ERC721BridgeInitiated", "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json deleted file mode 100644 index 0a5fddb96906..000000000000 --- a/packages/contracts-bedrock/snapshots/abi/L2OptimismMintableERC20Factory.json +++ /dev/null @@ -1,196 +0,0 @@ -[ - { - "inputs": [], - "name": "BRIDGE", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "bridge", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "string", - "name": "_symbol", - "type": "string" - } - ], - "name": "createOptimismMintableERC20", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "string", - "name": "_symbol", - "type": "string" - }, - { - "internalType": "uint8", - "name": "_decimals", - "type": "uint8" - } - ], - "name": "createOptimismMintableERC20WithDecimals", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "string", - "name": "_symbol", - "type": "string" - } - ], - "name": "createStandardL2Token", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "deployments", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "localToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "remoteToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "deployer", - "type": "address" - } - ], - "name": "OptimismMintableERC20Created", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "remoteToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "localToken", - "type": "address" - } - ], - "name": "StandardL2TokenCreated", - "type": "event" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json deleted file mode 100644 index 8dbba1e08992..000000000000 --- a/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json +++ /dev/null @@ -1,300 +0,0 @@ -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "addressManager", - "outputs": [ - { - "internalType": "contract IAddressManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_proxy", - "type": "address" - }, - { - "internalType": "address", - "name": "_newAdmin", - "type": "address" - } - ], - "name": "changeProxyAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_proxy", - "type": "address" - } - ], - "name": "getProxyAdmin", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_proxy", - "type": "address" - } - ], - "name": "getProxyImplementation", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "implementationName", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isUpgrading", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "proxyType", - "outputs": [ - { - "internalType": "enum ProxyAdmin.ProxyType", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "address", - "name": "_address", - "type": "address" - } - ], - "name": "setAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IAddressManager", - "name": "_address", - "type": "address" - } - ], - "name": "setAddressManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_address", - "type": "address" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - } - ], - "name": "setImplementationName", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_address", - "type": "address" - }, - { - "internalType": "enum ProxyAdmin.ProxyType", - "name": "_type", - "type": "uint8" - } - ], - "name": "setProxyType", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bool", - "name": "_upgrading", - "type": "bool" - } - ], - "name": "setUpgrading", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_proxy", - "type": "address" - }, - { - "internalType": "address", - "name": "_implementation", - "type": "address" - } - ], - "name": "upgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_proxy", - "type": "address" - }, - { - "internalType": "address", - "name": "_implementation", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - "name": "upgradeAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json b/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json index 44ecfa34094c..e562034818d5 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/L2StandardBridge.json @@ -1,4 +1,9 @@ [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "stateMutability": "payable", "type": "receive" @@ -21,7 +26,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } @@ -231,6 +236,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract StandardBridge", + "name": "_otherBridge", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "l1TokenBridge", @@ -254,7 +272,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -262,7 +280,7 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } @@ -548,6 +566,19 @@ "name": "ETHBridgeInitiated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json b/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json index 1d5f7aacfc7b..b2dc8dddd050 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/L2StandardBridgeInterop.json @@ -21,7 +21,7 @@ "name": "OTHER_BRIDGE", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } @@ -254,6 +254,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract StandardBridge", + "name": "_otherBridge", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "l1TokenBridge", @@ -277,7 +290,7 @@ "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -285,7 +298,7 @@ "name": "otherBridge", "outputs": [ { - "internalType": "contract IStandardBridge", + "internalType": "contract StandardBridge", "name": "", "type": "address" } @@ -602,6 +615,19 @@ "name": "ETHBridgeInitiated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, { "anonymous": false, "inputs": [ diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 7c858ca892ee..7c478feb235d 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -137,11 +137,6 @@ "internalType": "address", "name": "challenger", "type": "address" - }, - { - "internalType": "address", - "name": "systemConfigFeeAdmin", - "type": "address" } ], "internalType": "struct OPContractsManager.Roles", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json index 7c858ca892ee..7c478feb235d 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json @@ -137,11 +137,6 @@ "internalType": "address", "name": "challenger", "type": "address" - }, - { - "internalType": "address", - "name": "systemConfigFeeAdmin", - "type": "address" } ], "internalType": "struct OPContractsManager.Roles", diff --git a/packages/contracts-bedrock/snapshots/abi/L1OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC20Factory.json similarity index 100% rename from packages/contracts-bedrock/snapshots/abi/L1OptimismMintableERC20Factory.json rename to packages/contracts-bedrock/snapshots/abi/OptimismMintableERC20Factory.json diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json index 58e3271abc92..35250255a9ba 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721.json @@ -2,7 +2,7 @@ { "inputs": [ { - "internalType": "contract IL2ERC721Bridge", + "internalType": "address", "name": "_bridge", "type": "address" }, @@ -35,7 +35,7 @@ "name": "BRIDGE", "outputs": [ { - "internalType": "contract IL2ERC721Bridge", + "internalType": "address", "name": "", "type": "address" } @@ -124,7 +124,7 @@ "name": "bridge", "outputs": [ { - "internalType": "contract IL2ERC721Bridge", + "internalType": "address", "name": "", "type": "address" } diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json index 0137a7bb20ae..e6ecac1d9381 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismMintableERC721Factory.json @@ -1,15 +1,31 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_bridge", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_remoteChainId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [], "name": "BRIDGE", "outputs": [ { - "internalType": "contract IL2ERC721Bridge", + "internalType": "address", "name": "", "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -30,12 +46,12 @@ "name": "bridge", "outputs": [ { - "internalType": "contract IL2ERC721Bridge", + "internalType": "address", "name": "", "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -88,7 +104,7 @@ }, { "inputs": [], - "name": "remoteChainId", + "name": "remoteChainID", "outputs": [ { "internalType": "uint256", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json index ed8ec82591ed..7ccee328a984 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal.json @@ -413,17 +413,27 @@ { "inputs": [ { - "internalType": "enum Types.ConfigType", - "name": "_type", + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", "type": "uint8" }, { - "internalType": "bytes", - "name": "_value", - "type": "bytes" + "internalType": "bytes32", + "name": "_name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_symbol", + "type": "bytes32" } ], - "name": "setConfig", + "name": "setGasPayingToken", "outputs": [], "stateMutability": "nonpayable", "type": "function" diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index b4bd5efa8df3..2f52ed573d37 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -605,17 +605,27 @@ { "inputs": [ { - "internalType": "enum Types.ConfigType", - "name": "_type", + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", "type": "uint8" }, { - "internalType": "bytes", - "name": "_value", - "type": "bytes" + "internalType": "bytes32", + "name": "_name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_symbol", + "type": "bytes32" } ], - "name": "setConfig", + "name": "setGasPayingToken", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -659,24 +669,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "_gasLimit", - "type": "uint32" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - } - ], - "name": "upgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json index b4bd5efa8df3..5b9f72b9446c 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json @@ -605,7 +605,7 @@ { "inputs": [ { - "internalType": "enum Types.ConfigType", + "internalType": "enum ConfigType", "name": "_type", "type": "uint8" }, @@ -620,6 +620,34 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_symbol", + "type": "bytes32" + } + ], + "name": "setGasPayingToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -659,24 +687,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "_gasLimit", - "type": "uint32" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - } - ], - "name": "upgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json b/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json index 6412128462e0..700e7d7b9810 100644 --- a/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json +++ b/packages/contracts-bedrock/snapshots/abi/SequencerFeeVault.json @@ -1,4 +1,25 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_minWithdrawalAmount", + "type": "uint256" + }, + { + "internalType": "enum Types.WithdrawalNetwork", + "name": "_withdrawalNetwork", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "stateMutability": "payable", "type": "receive" @@ -35,30 +56,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "config", - "outputs": [ - { - "internalType": "address", - "name": "recipient_", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount_", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "", "type": "uint8" } ], @@ -71,7 +69,7 @@ "outputs": [ { "internalType": "address", - "name": "recipient_", + "name": "", "type": "address" } ], @@ -143,7 +141,7 @@ "outputs": [ { "internalType": "enum Types.WithdrawalNetwork", - "name": "withdrawalNetwork_", + "name": "network_", "type": "uint8" } ], diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json index e61f005d9aa2..0304f507eb50 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json @@ -30,19 +30,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "UPGRADER_SLOT", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "guardian", @@ -63,11 +50,6 @@ "name": "_guardian", "type": "address" }, - { - "internalType": "address", - "name": "_upgrader", - "type": "address" - }, { "internalType": "bool", "name": "_paused", @@ -112,19 +94,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "upgrader", - "outputs": [ - { - "internalType": "address", - "name": "upgrader_", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "version", diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json index 7f086dfad600..b7e18556fa2c 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json @@ -225,19 +225,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "feeAdmin", - "outputs": [ - { - "internalType": "address", - "name": "addr_", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "gasLimit", @@ -298,31 +285,9 @@ { "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "feeAdmin", - "type": "address" - }, - { - "internalType": "address", - "name": "unsafeBlockSigner", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "batcherHash", - "type": "bytes32" - } - ], - "internalType": "struct SystemConfig.Roles", - "name": "_roles", - "type": "tuple" + "internalType": "address", + "name": "_owner", + "type": "address" }, { "internalType": "uint32", @@ -334,11 +299,21 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, + { + "internalType": "bytes32", + "name": "_batcherHash", + "type": "bytes32" + }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, + { + "internalType": "address", + "name": "_unsafeBlockSigner", + "type": "address" + }, { "components": [ { @@ -655,34 +630,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum Types.ConfigType", - "name": "_type", - "type": "uint8" - }, - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_min", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "_network", - "type": "uint8" - } - ], - "name": "setFeeVaultConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -853,10 +800,5 @@ ], "name": "OwnershipTransferred", "type": "event" - }, - { - "inputs": [], - "name": "UnsafeCast", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json index a0fc03aa0b3b..a459af15801b 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json @@ -246,19 +246,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "feeAdmin", - "outputs": [ - { - "internalType": "address", - "name": "addr_", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "gasLimit", @@ -319,31 +306,9 @@ { "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "feeAdmin", - "type": "address" - }, - { - "internalType": "address", - "name": "unsafeBlockSigner", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "batcherHash", - "type": "bytes32" - } - ], - "internalType": "struct SystemConfig.Roles", - "name": "_roles", - "type": "tuple" + "internalType": "address", + "name": "_owner", + "type": "address" }, { "internalType": "uint32", @@ -355,11 +320,21 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, + { + "internalType": "bytes32", + "name": "_batcherHash", + "type": "bytes32" + }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, + { + "internalType": "address", + "name": "_unsafeBlockSigner", + "type": "address" + }, { "components": [ { @@ -443,6 +418,11 @@ "internalType": "struct SystemConfig.Addresses", "name": "_addresses", "type": "tuple" + }, + { + "internalType": "address", + "name": "_dependencyManager", + "type": "address" } ], "name": "initialize", @@ -453,31 +433,9 @@ { "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "feeAdmin", - "type": "address" - }, - { - "internalType": "address", - "name": "unsafeBlockSigner", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "batcherHash", - "type": "bytes32" - } - ], - "internalType": "struct SystemConfig.Roles", - "name": "_roles", - "type": "tuple" + "internalType": "address", + "name": "_owner", + "type": "address" }, { "internalType": "uint32", @@ -489,11 +447,21 @@ "name": "_blobbasefeeScalar", "type": "uint32" }, + { + "internalType": "bytes32", + "name": "_batcherHash", + "type": "bytes32" + }, { "internalType": "uint64", "name": "_gasLimit", "type": "uint64" }, + { + "internalType": "address", + "name": "_unsafeBlockSigner", + "type": "address" + }, { "components": [ { @@ -577,11 +545,6 @@ "internalType": "struct SystemConfig.Addresses", "name": "_addresses", "type": "tuple" - }, - { - "internalType": "address", - "name": "_dependencyManager", - "type": "address" } ], "name": "initialize", @@ -828,34 +791,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum Types.ConfigType", - "name": "_type", - "type": "uint8" - }, - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_min", - "type": "uint256" - }, - { - "internalType": "enum Types.WithdrawalNetwork", - "name": "_network", - "type": "uint8" - } - ], - "name": "setFeeVaultConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -1026,10 +961,5 @@ ], "name": "OwnershipTransferred", "type": "event" - }, - { - "inputs": [], - "name": "UnsafeCast", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json new file mode 100644 index 000000000000..9bc33d142741 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer0.json @@ -0,0 +1,9 @@ +[ + { + "bytes": "20", + "label": "spacer_0_0_20", + "offset": 0, + "slot": "0", + "type": "address" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json new file mode 100644 index 000000000000..ff157f517694 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/CrossDomainMessengerLegacySpacer1.json @@ -0,0 +1,65 @@ +[ + { + "bytes": "1600", + "label": "spacer_1_0_1600", + "offset": 0, + "slot": "0", + "type": "uint256[50]" + }, + { + "bytes": "20", + "label": "spacer_51_0_20", + "offset": 0, + "slot": "50", + "type": "address" + }, + { + "bytes": "1568", + "label": "spacer_52_0_1568", + "offset": 0, + "slot": "51", + "type": "uint256[49]" + }, + { + "bytes": "1", + "label": "spacer_101_0_1", + "offset": 0, + "slot": "100", + "type": "bool" + }, + { + "bytes": "1568", + "label": "spacer_102_0_1568", + "offset": 0, + "slot": "101", + "type": "uint256[49]" + }, + { + "bytes": "32", + "label": "spacer_151_0_32", + "offset": 0, + "slot": "150", + "type": "uint256" + }, + { + "bytes": "1568", + "label": "spacer_152_0_1568", + "offset": 0, + "slot": "151", + "type": "uint256[49]" + }, + { + "bytes": "32", + "label": "spacer_201_0_32", + "offset": 0, + "slot": "200", + "type": "mapping(bytes32 => bool)" + }, + { + "bytes": "32", + "label": "spacer_202_0_32", + "offset": 0, + "slot": "201", + "type": "mapping(bytes32 => bool)" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json index be59f51e2f9a..2928d2147b5c 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json @@ -75,12 +75,5 @@ "offset": 0, "slot": "7", "type": "uint256" - }, - { - "bytes": "1", - "label": "isIsthmus", - "offset": 0, - "slot": "8", - "type": "bool" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json index 39a149f0d6e2..14ee2ff9609a 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json @@ -76,18 +76,11 @@ "slot": "7", "type": "uint256" }, - { - "bytes": "1", - "label": "isIsthmus", - "offset": 0, - "slot": "8", - "type": "bool" - }, { "bytes": "64", "label": "dependencySet", "offset": 0, - "slot": "9", + "slot": "8", "type": "struct EnumerableSet.UintSet" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json index ec29a4be2bfe..c68ec541baba 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1CrossDomainMessenger.json @@ -7,11 +7,18 @@ "type": "address" }, { - "bytes": "12", - "label": "spacer_0_20_12", + "bytes": "1", + "label": "_initialized", "offset": 20, "slot": "0", - "type": "bytes12" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 21, + "slot": "0", + "type": "bool" }, { "bytes": "1600", @@ -85,10 +92,10 @@ }, { "bytes": "20", - "label": "spacer_204_0_20", + "label": "xDomainMsgSender", "offset": 0, "slot": "204", - "type": "bytes20" + "type": "address" }, { "bytes": "30", @@ -106,52 +113,17 @@ }, { "bytes": "20", - "label": "spacer_207_0_20", + "label": "otherMessenger", "offset": 0, "slot": "207", - "type": "address" - }, - { - "bytes": "20", - "label": "xDomainMsgSender", - "offset": 0, - "slot": "208", - "type": "address" + "type": "contract CrossDomainMessenger" }, { - "bytes": "12", - "label": "spacer_208_20_12", - "offset": 20, - "slot": "208", - "type": "bytes12" - }, - { - "bytes": "1312", + "bytes": "1376", "label": "__gap", "offset": 0, - "slot": "209", - "type": "uint256[41]" - }, - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "250", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "250", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_250_2_30", - "offset": 2, - "slot": "250", - "type": "bytes30" + "slot": "208", + "type": "uint256[43]" }, { "bytes": "20", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json index cea792454f7b..2c14ad25904b 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1ERC721Bridge.json @@ -1,24 +1,38 @@ [ { - "bytes": "32", - "label": "spacer_0_0_32", + "bytes": "1", + "label": "_initialized", "offset": 0, "slot": "0", - "type": "bytes32" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_0_2_30", + "offset": 2, + "slot": "0", + "type": "bytes30" }, { "bytes": "20", - "label": "spacer_1_0_20", + "label": "messenger", "offset": 0, "slot": "1", - "type": "address" + "type": "contract ICrossDomainMessenger" }, { "bytes": "20", - "label": "spacer_2_0_20", + "label": "otherBridge", "offset": 0, "slot": "2", - "type": "address" + "type": "contract ERC721Bridge" }, { "bytes": "1472", @@ -40,12 +54,5 @@ "offset": 0, "slot": "50", "type": "contract ISuperchainConfig" - }, - { - "bytes": "20", - "label": "crossDomainMessenger", - "offset": 0, - "slot": "51", - "type": "contract ICrossDomainMessenger" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json index 99875ccd2c6c..5562a214e4fe 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1StandardBridge.json @@ -1,10 +1,24 @@ [ { - "bytes": "32", - "label": "spacer_0_0_32", + "bytes": "1", + "label": "_initialized", "offset": 0, "slot": "0", - "type": "bytes32" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_0_2_30", + "offset": 2, + "slot": "0", + "type": "bytes30" }, { "bytes": "20", @@ -22,45 +36,24 @@ }, { "bytes": "20", - "label": "spacer_3_0_20", + "label": "messenger", "offset": 0, "slot": "3", - "type": "address" + "type": "contract ICrossDomainMessenger" }, { "bytes": "20", - "label": "spacer_4_0_20", + "label": "otherBridge", "offset": 0, "slot": "4", - "type": "address" + "type": "contract StandardBridge" }, { - "bytes": "1408", + "bytes": "1440", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[44]" - }, - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "49", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "49", - "type": "bool" - }, - { - "bytes": "30", - "label": "spacer_49_2_30", - "offset": 2, - "slot": "49", - "type": "bytes30" + "type": "uint256[45]" }, { "bytes": "20", @@ -75,12 +68,5 @@ "offset": 0, "slot": "51", "type": "contract ISystemConfig" - }, - { - "bytes": "20", - "label": "crossDomainMessenger", - "offset": 0, - "slot": "52", - "type": "contract ICrossDomainMessenger" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json index beae81a1fe17..dec784b5a32e 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2CrossDomainMessenger.json @@ -7,11 +7,18 @@ "type": "address" }, { - "bytes": "12", - "label": "spacer_0_20_12", + "bytes": "1", + "label": "_initialized", "offset": 20, "slot": "0", - "type": "bytes12" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 21, + "slot": "0", + "type": "bool" }, { "bytes": "1600", @@ -85,10 +92,10 @@ }, { "bytes": "20", - "label": "spacer_204_0_20", + "label": "xDomainMsgSender", "offset": 0, "slot": "204", - "type": "bytes20" + "type": "address" }, { "bytes": "30", @@ -106,30 +113,16 @@ }, { "bytes": "20", - "label": "spacer_207_0_20", + "label": "otherMessenger", "offset": 0, "slot": "207", - "type": "address" + "type": "contract CrossDomainMessenger" }, { - "bytes": "20", - "label": "xDomainMsgSender", - "offset": 0, - "slot": "208", - "type": "address" - }, - { - "bytes": "12", - "label": "spacer_208_20_12", - "offset": 20, - "slot": "208", - "type": "bytes12" - }, - { - "bytes": "1312", + "bytes": "1376", "label": "__gap", "offset": 0, - "slot": "209", - "type": "uint256[41]" + "slot": "208", + "type": "uint256[43]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json index ad9709095c33..546b37ba6398 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2ERC721Bridge.json @@ -1,24 +1,38 @@ [ { - "bytes": "32", - "label": "spacer_0_0_32", + "bytes": "1", + "label": "_initialized", "offset": 0, "slot": "0", - "type": "bytes32" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_0_2_30", + "offset": 2, + "slot": "0", + "type": "bytes30" }, { "bytes": "20", - "label": "spacer_1_0_20", + "label": "messenger", "offset": 0, "slot": "1", - "type": "address" + "type": "contract ICrossDomainMessenger" }, { "bytes": "20", - "label": "spacer_2_0_20", + "label": "otherBridge", "offset": 0, "slot": "2", - "type": "address" + "type": "contract ERC721Bridge" }, { "bytes": "1472", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json deleted file mode 100644 index 75e6d15b1f7d..000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2OptimismMintableERC20Factory.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "bytes": "32", - "label": "spacer_0_0_32", - "offset": 0, - "slot": "0", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "spacer_1_0_32", - "offset": 0, - "slot": "1", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "deployments", - "offset": 0, - "slot": "2", - "type": "mapping(address => address)" - }, - { - "bytes": "1536", - "label": "__gap", - "offset": 0, - "slot": "3", - "type": "uint256[48]" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json deleted file mode 100644 index a0b6f46bf85e..000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "bytes": "20", - "label": "_owner", - "offset": 0, - "slot": "0", - "type": "address" - }, - { - "bytes": "32", - "label": "proxyType", - "offset": 0, - "slot": "1", - "type": "mapping(address => enum ProxyAdmin.ProxyType)" - }, - { - "bytes": "32", - "label": "implementationName", - "offset": 0, - "slot": "2", - "type": "mapping(address => string)" - }, - { - "bytes": "20", - "label": "addressManager", - "offset": 0, - "slot": "3", - "type": "contract IAddressManager" - }, - { - "bytes": "1", - "label": "upgrading", - "offset": 20, - "slot": "3", - "type": "bool" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json index bda39d85dc77..c6ccc0fc2e03 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridge.json @@ -1,10 +1,24 @@ [ { - "bytes": "32", - "label": "spacer_0_0_32", + "bytes": "1", + "label": "_initialized", "offset": 0, "slot": "0", - "type": "bytes32" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_0_2_30", + "offset": 2, + "slot": "0", + "type": "bytes30" }, { "bytes": "20", @@ -22,23 +36,23 @@ }, { "bytes": "20", - "label": "spacer_3_0_20", + "label": "messenger", "offset": 0, "slot": "3", - "type": "address" + "type": "contract ICrossDomainMessenger" }, { "bytes": "20", - "label": "spacer_4_0_20", + "label": "otherBridge", "offset": 0, "slot": "4", - "type": "address" + "type": "contract StandardBridge" }, { - "bytes": "1408", + "bytes": "1440", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[44]" + "type": "uint256[45]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json index bda39d85dc77..c6ccc0fc2e03 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2StandardBridgeInterop.json @@ -1,10 +1,24 @@ [ { - "bytes": "32", - "label": "spacer_0_0_32", + "bytes": "1", + "label": "_initialized", "offset": 0, "slot": "0", - "type": "bytes32" + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "30", + "label": "spacer_0_2_30", + "offset": 2, + "slot": "0", + "type": "bytes30" }, { "bytes": "20", @@ -22,23 +36,23 @@ }, { "bytes": "20", - "label": "spacer_3_0_20", + "label": "messenger", "offset": 0, "slot": "3", - "type": "address" + "type": "contract ICrossDomainMessenger" }, { "bytes": "20", - "label": "spacer_4_0_20", + "label": "otherBridge", "offset": 0, "slot": "4", - "type": "address" + "type": "contract StandardBridge" }, { - "bytes": "1408", + "bytes": "1440", "label": "__gap", "offset": 0, "slot": "5", - "type": "uint256[44]" + "type": "uint256[45]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json b/packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json similarity index 64% rename from packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json rename to packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json index 5bcde5813268..94c133d105d2 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1OptimismMintableERC20Factory.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/OptimismMintableERC20Factory.json @@ -1,58 +1,44 @@ [ - { - "bytes": "32", - "label": "spacer_0_0_32", - "offset": 0, - "slot": "0", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "spacer_1_0_32", - "offset": 0, - "slot": "1", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "deployments", - "offset": 0, - "slot": "2", - "type": "mapping(address => address)" - }, - { - "bytes": "1536", - "label": "__gap", - "offset": 0, - "slot": "3", - "type": "uint256[48]" - }, { "bytes": "1", "label": "_initialized", "offset": 0, - "slot": "51", + "slot": "0", "type": "uint8" }, { "bytes": "1", "label": "_initializing", "offset": 1, - "slot": "51", + "slot": "0", "type": "bool" }, { "bytes": "30", - "label": "spacer_51_2_30", + "label": "spacer_0_2_30", "offset": 2, - "slot": "51", + "slot": "0", "type": "bytes30" }, { "bytes": "20", - "label": "standardBridge", + "label": "bridge", "offset": 0, - "slot": "52", + "slot": "1", "type": "address" + }, + { + "bytes": "32", + "label": "deployments", + "offset": 0, + "slot": "2", + "type": "mapping(address => address)" + }, + { + "bytes": "1536", + "label": "__gap", + "offset": 0, + "slot": "3", + "type": "uint256[48]" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol index f5a249bfda65..27be4a7332fa 100644 --- a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; // Contracts import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; -import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -19,10 +18,7 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; /// @notice The L1CrossDomainMessenger is a message passing interface between L1 and L2 responsible /// for sending and receiving data on the L1 side. Users are encouraged to use this /// interface instead of interacting with lower-level contracts directly. -contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver, Initializable { - /// @notice Spacer to give the initializer a full slot, to avoid offsetting the superchainConfig slot. - bytes30 private spacer_250_2_30; - +contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver { /// @notice Contract of the SuperchainConfig. ISuperchainConfig public superchainConfig; @@ -34,12 +30,16 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver, Initializable ISystemConfig public systemConfig; /// @notice Semantic version. - /// @custom:semver 2.4.1-beta.3 - string public constant version = "2.4.1-beta.3"; + /// @custom:semver 2.4.1-beta.2 + string public constant version = "2.4.1-beta.2"; /// @notice Constructs the L1CrossDomainMessenger contract. constructor() CrossDomainMessenger() { - _disableInitializers(); + initialize({ + _superchainConfig: ISuperchainConfig(address(0)), + _portal: IOptimismPortal(payable(address(0))), + _systemConfig: ISystemConfig(address(0)) + }); } /// @notice Initializes the contract. @@ -57,12 +57,7 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver, Initializable superchainConfig = _superchainConfig; portal = _portal; systemConfig = _systemConfig; - } - - /// @notice Getter function for the other messenger. - /// @return Contract of the messenger on the other network. - function otherMessenger() public pure override returns (CrossDomainMessenger) { - return CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + __CrossDomainMessenger_init({ _otherMessenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER) }); } /// @inheritdoc CrossDomainMessenger @@ -91,7 +86,7 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver, Initializable /// @inheritdoc CrossDomainMessenger function _isOtherMessenger() internal view override returns (bool) { - return msg.sender == address(portal) && portal.l2Sender() == address(otherMessenger()); + return msg.sender == address(portal) && portal.l2Sender() == address(otherMessenger); } /// @inheritdoc CrossDomainMessenger diff --git a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol index f66c46c67d71..bd9b31c1e589 100644 --- a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.25; +pragma solidity 0.8.15; // Contracts import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; -import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -20,7 +19,7 @@ import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; /// @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to /// make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract /// acts as an escrow for ERC721 tokens deposited into L2. -contract L1ERC721Bridge is ERC721Bridge, Initializable, ISemver { +contract L1ERC721Bridge is ERC721Bridge, ISemver { /// @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token /// by ID was deposited for a given L2 token. mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; @@ -28,16 +27,13 @@ contract L1ERC721Bridge is ERC721Bridge, Initializable, ISemver { /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; - /// @notice Contract of the CrossDomainMessenger on this chain. - ICrossDomainMessenger internal crossDomainMessenger; - /// @notice Semantic version. - /// @custom:semver 2.1.1-beta.3 - string public constant version = "2.1.1-beta.3"; + /// @custom:semver 2.2.0-beta.1 + string public constant version = "2.2.0-beta.1"; /// @notice Constructs the L1ERC721Bridge contract. constructor() ERC721Bridge() { - _disableInitializers(); + initialize({ _messenger: ICrossDomainMessenger(address(0)), _superchainConfig: ISuperchainConfig(address(0)) }); } /// @notice Initializes the contract. @@ -45,19 +41,7 @@ contract L1ERC721Bridge is ERC721Bridge, Initializable, ISemver { /// @param _superchainConfig Contract of the SuperchainConfig contract on this network. function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) public initializer { superchainConfig = _superchainConfig; - crossDomainMessenger = _messenger; - } - - /// @notice Getter function for the CrossDomainMessenger contract on this chain. - /// @return Contract of the CrossDomainMessenger on this chain. - function messenger() public view override returns (ICrossDomainMessenger) { - return ICrossDomainMessenger(crossDomainMessenger); - } - - /// @notice Getter function for the other bridge. - /// @return Contract of the bridge on the other network. - function otherBridge() public pure override returns (ERC721Bridge) { - return ERC721Bridge(payable(Predeploys.L2_ERC721_BRIDGE)); + __ERC721Bridge_init({ _messenger: _messenger, _otherBridge: ERC721Bridge(payable(Predeploys.L2_ERC721_BRIDGE)) }); } /// @inheritdoc ERC721Bridge @@ -132,7 +116,7 @@ contract L1ERC721Bridge is ERC721Bridge, Initializable, ISemver { IERC721(_localToken).transferFrom({ from: _from, to: address(this), tokenId: _tokenId }); // Send calldata into L2 - messenger().sendMessage({ _target: address(otherBridge()), _message: message, _minGasLimit: _minGasLimit }); + messenger.sendMessage({ _target: address(otherBridge), _message: message, _minGasLimit: _minGasLimit }); emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); } } diff --git a/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol deleted file mode 100644 index 8d70325597bc..000000000000 --- a/packages/contracts-bedrock/src/L1/L1OptimismMintableERC20Factory.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; - -/// @custom:proxied true -/// @title L1OptimismMintableERC20Factory -/// @notice Allows users to create L1 tokens that represent L2 native tokens. -contract L1OptimismMintableERC20Factory is OptimismMintableERC20Factory, Initializable { - /// @custom:semver 1.3.1-beta.5 - /// @notice Semantic version. - /// The semver MUST be bumped any time that there is a change in - /// the OptimismMintableERC20 token contract since this contract - /// is responsible for deploying OptimismMintableERC20 contracts. - string public constant version = "1.3.1-beta.5"; - - /// @custom:spacer - /// @notice Spacer to fill the remainder of the _initialized slot, preventing the standardBridge - /// address from being packed with it. - bytes30 private spacer_51_2_30; - - /// @notice Address of the bridge on this domain. - address internal standardBridge; - - constructor() { - _disableInitializers(); - } - - /// @notice Initializes the contract. - /// @param _bridge Contract of the bridge on this domain. - function initialize(address _bridge) public initializer { - standardBridge = _bridge; - } - - /// @notice Getter function for the bridge contract. - /// @return Contract of the bridge on this domain. - function bridge() public view virtual override returns (address) { - return standardBridge; - } -} diff --git a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol index f49f29864a93..54cc833fe673 100644 --- a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol @@ -3,16 +3,15 @@ pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; + // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; /// @custom:proxied true /// @title L1StandardBridge @@ -24,7 +23,7 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable /// NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples /// of some token types that may not be properly supported by this contract include, but are /// not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists. -contract L1StandardBridge is StandardBridge, ISemver, Initializable { +contract L1StandardBridge is StandardBridge, ISemver { /// @custom:legacy /// @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated. /// @param from Address of the depositor. @@ -76,13 +75,8 @@ contract L1StandardBridge is StandardBridge, ISemver, Initializable { ); /// @notice Semantic version. - /// @custom:semver 2.2.1-beta.3 - string public constant version = "2.2.1-beta.3"; - - /// @custom:spacer - /// @notice Spacer to fill the remainder of the _initialized slot, preventing the superchainConfig - /// address from being packed with it. - bytes30 private spacer_49_2_30; + /// @custom:semver 2.2.1-beta.2 + string public constant version = "2.2.1-beta.2"; /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; @@ -90,12 +84,13 @@ contract L1StandardBridge is StandardBridge, ISemver, Initializable { /// @notice Address of the SystemConfig contract. ISystemConfig public systemConfig; - /// @notice Contract for the CrossDomainMessenger on this network. - ICrossDomainMessenger internal crossDomainMessenger; - /// @notice Constructs the L1StandardBridge contract. constructor() StandardBridge() { - _disableInitializers(); + initialize({ + _messenger: ICrossDomainMessenger(address(0)), + _superchainConfig: ISuperchainConfig(address(0)), + _systemConfig: ISystemConfig(address(0)) + }); } /// @notice Initializer. @@ -111,19 +106,10 @@ contract L1StandardBridge is StandardBridge, ISemver, Initializable { { superchainConfig = _superchainConfig; systemConfig = _systemConfig; - crossDomainMessenger = _messenger; - } - - /// @notice Returns the contract of the bridge on the other chain. - /// @return Contract of the bridge on the other chain. - function otherBridge() public pure override returns (IStandardBridge) { - return IStandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)); - } - - /// @notice Getter function for the messenger contract. - /// @return Contract of the messenger on this domain. - function messenger() public view override returns (ICrossDomainMessenger) { - return ICrossDomainMessenger(crossDomainMessenger); + __StandardBridge_init({ + _messenger: _messenger, + _otherBridge: StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)) + }); } /// @inheritdoc StandardBridge @@ -255,8 +241,8 @@ contract L1StandardBridge is StandardBridge, ISemver, Initializable { /// @custom:legacy /// @notice Retrieves the access of the corresponding L2 bridge contract. /// @return Address of the corresponding L2 bridge contract. - function l2TokenBridge() external pure returns (address) { - return address(otherBridge()); + function l2TokenBridge() external view returns (address) { + return address(otherBridge); } /// @notice Internal function for initiating an ETH deposit. diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 721c7649ed0a..4bf52ff228a1 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -46,7 +46,6 @@ contract OPContractsManager is ISemver, Initializable { address unsafeBlockSigner; address proposer; address challenger; - address systemConfigFeeAdmin; } /// @notice The full set of inputs to deploy a new OP Stack chain. @@ -130,8 +129,8 @@ contract OPContractsManager is ISemver, Initializable { // -------- Constants and Variables -------- - /// @custom:semver 1.0.0-beta.21 - string public constant version = "1.0.0-beta.21"; + /// @custom:semver 1.0.0-beta.20 + string public constant version = "1.0.0-beta.20"; /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. @@ -255,7 +254,7 @@ contract OPContractsManager is ISemver, Initializable { output.systemConfigProxy = ISystemConfig(deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "SystemConfig")); output.optimismMintableERC20FactoryProxy = IOptimismMintableERC20Factory( - deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "L1OptimismMintableERC20Factory") + deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "OptimismMintableERC20Factory") ); output.disputeGameFactoryProxy = IDisputeGameFactory(deployProxy(l2ChainId, output.opChainProxyAdmin, saltMixer, "DisputeGameFactory")); @@ -469,15 +468,12 @@ contract OPContractsManager is ISemver, Initializable { return abi.encodeWithSelector( _selector, - ISystemConfig.Roles({ - owner: _input.roles.systemConfigOwner, - feeAdmin: _input.roles.systemConfigFeeAdmin, - unsafeBlockSigner: _input.roles.unsafeBlockSigner, - batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) - }), + _input.roles.systemConfigOwner, _input.basefeeScalar, _input.blobBasefeeScalar, + bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, + _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs @@ -491,15 +487,12 @@ contract OPContractsManager is ISemver, Initializable { return abi.encodeWithSelector( _selector, - ISystemConfig.Roles({ - owner: _input.roles.systemConfigOwner, - feeAdmin: _input.roles.systemConfigFeeAdmin, - unsafeBlockSigner: _input.roles.unsafeBlockSigner, - batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) - }), + _input.roles.systemConfigOwner, _input.basefeeScalar, _input.blobBasefeeScalar, + bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, + _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol index a237dc1162f0..19de5537b41c 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol @@ -41,15 +41,12 @@ contract OPContractsManagerInterop is OPContractsManager { return abi.encodeWithSelector( _selector, - ISystemConfig.Roles({ - owner: _input.roles.systemConfigOwner, - feeAdmin: _input.roles.systemConfigFeeAdmin, - unsafeBlockSigner: _input.roles.unsafeBlockSigner, - batcherHash: bytes32(uint256(uint160(_input.roles.batcher))) - }), + _input.roles.systemConfigOwner, _input.basefeeScalar, _input.blobBasefeeScalar, + bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash _input.gasLimit, + _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs, diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal.sol b/packages/contracts-bedrock/src/L1/OptimismPortal.sol index d097d2e22d12..2f9f53787581 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal.sol @@ -146,9 +146,9 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { } /// @notice Semantic version. - /// @custom:semver 2.8.1-beta.5 + /// @custom:semver 2.8.1-beta.4 function version() public pure virtual returns (string memory) { - return "2.8.1-beta.5"; + return "2.8.1-beta.4"; } /// @notice Constructs the OptimismPortal contract. @@ -584,17 +584,17 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); } - /// @notice Sets static configuration options for the L2 system. - /// @param _type Type of configuration to set. - /// @param _value Encoded value of the configuration. - function setConfig(Types.ConfigType _type, bytes memory _value) external { + /// @notice Sets the gas paying token for the L2 system. This token is used as the + /// L2 native asset. Only the SystemConfig contract can call this function. + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { if (msg.sender != address(systemConfig)) revert Unauthorized(); // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setConfig`. + // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. + // Emit the special deposit transaction directly that sets the gas paying + // token in the L1Block predeploy contract. emit TransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, @@ -604,7 +604,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver { uint256(0), // value uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (_type, _value)) + abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) ) ); } diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 951e2bea4a18..985711ed18e5 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -183,9 +183,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { } /// @notice Semantic version. - /// @custom:semver 3.11.0-beta.7 + /// @custom:semver 3.11.0-beta.6 function version() public pure virtual returns (string memory) { - return "3.11.0-beta.7"; + return "3.11.0-beta.6"; } /// @notice Constructs the OptimismPortal contract. @@ -605,17 +605,17 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); } - /// @notice Sets static configuration options for the L2 system. - /// @param _type Type of configuration to set. - /// @param _value Encoded value of the configuration. - function setConfig(Types.ConfigType _type, bytes memory _value) external { + /// @notice Sets the gas paying token for the L2 system. This token is used as the + /// L2 native asset. Only the SystemConfig contract can call this function. + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { if (msg.sender != address(systemConfig)) revert Unauthorized(); // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setConfig`. + // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. + // Emit the special deposit transaction directly that sets the gas paying + // token in the L1Block predeploy contract. emit TransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, @@ -625,30 +625,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { uint256(0), // value uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (_type, _value)) - ) - ); - } - - /// @notice Calls the L2ProxyAdmin as the DEPOSITOR_ACCOUNT. This function can be used - /// to upgrade the predeploys on L2. Only callable by the upgrader role on the - /// SuperchainConfig. - function upgrade(uint32 _gasLimit, bytes memory _calldata) external { - if (msg.sender != superchainConfig.upgrader()) revert Unauthorized(); - - useGas(_gasLimit); - - // Emit the special deposit transaction which calls to the L2 Proxy Admin - emit TransactionDeposited( - Constants.DEPOSITOR_ACCOUNT, - Predeploys.PROXY_ADMIN, - DEPOSIT_VERSION, - abi.encodePacked( - uint256(0), // mint - uint256(0), // value - uint64(_gasLimit), // gasLimit - false, // isCreation, - _calldata // data + abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) ) ); } diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index be23f4eec56d..4c238c415d34 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -3,6 +3,12 @@ pragma solidity 0.8.15; // Contracts import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; +import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; + +// Libraries +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { Constants } from "src/libraries/Constants.sol"; +import { Unauthorized } from "src/libraries/PortalErrors.sol"; /// @custom:proxied true /// @title OptimismPortalInterop @@ -17,8 +23,33 @@ contract OptimismPortalInterop is OptimismPortal2 { OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) { } - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); + return string.concat(super.version(), "+interop-beta.2"); + } + + /// @notice Sets static configuration options for the L2 system. + /// @param _type Type of configuration to set. + /// @param _value Encoded value of the configuration. + function setConfig(ConfigType _type, bytes memory _value) external { + if (msg.sender != address(systemConfig)) revert Unauthorized(); + + // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. + // This value must be large enough to cover the cost of calling `L1Block.setConfig`. + useGas(SYSTEM_DEPOSIT_GAS_LIMIT); + + // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. + emit TransactionDeposited( + Constants.DEPOSITOR_ACCOUNT, + Predeploys.L1_BLOCK_ATTRIBUTES, + DEPOSIT_VERSION, + abi.encodePacked( + uint256(0), // mint + uint256(0), // value + uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit + false, // isCreation, + abi.encodeCall(L1BlockInterop.setConfig, (_type, _value)) + ) + ); } } diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index 1701bcff165b..51b13936c81b 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -12,10 +12,8 @@ import { Storage } from "src/libraries/Storage.sol"; contract SuperchainConfig is Initializable, ISemver { /// @notice Enum representing different types of updates. /// @custom:value GUARDIAN Represents an update to the guardian. - /// @custom:value UPGRADER Represents an update to the upgrader. enum UpdateType { - GUARDIAN, - UPGRADER + GUARDIAN } /// @notice Whether or not the Superchain is paused. @@ -25,10 +23,6 @@ contract SuperchainConfig is Initializable, ISemver { /// It can only be modified by an upgrade. bytes32 public constant GUARDIAN_SLOT = bytes32(uint256(keccak256("superchainConfig.guardian")) - 1); - /// @notice The address of the upgrader, which can faciliate upgrades of L2 predeploys. - /// . It can only be modified by an upgrade. - bytes32 public constant UPGRADER_SLOT = bytes32(uint256(keccak256("superchainConfig.upgrader")) - 1); - /// @notice Emitted when the pause is triggered. /// @param identifier A string helping to identify provenance of the pause transaction. event Paused(string identifier); @@ -42,20 +36,19 @@ contract SuperchainConfig is Initializable, ISemver { event ConfigUpdate(UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 1.1.1-beta.2 - string public constant version = "1.1.1-beta.2"; + /// @custom:semver 1.1.1-beta.1 + string public constant version = "1.1.1-beta.1"; /// @notice Constructs the SuperchainConfig contract. constructor() { - _disableInitializers(); + initialize({ _guardian: address(0), _paused: false }); } /// @notice Initializer. /// @param _guardian Address of the guardian, can pause the OptimismPortal. /// @param _paused Initial paused status. - function initialize(address _guardian, address _upgrader, bool _paused) public initializer { + function initialize(address _guardian, bool _paused) public initializer { _setGuardian(_guardian); - _setUpgrader(_upgrader); if (_paused) { _pause("Initializer paused"); } @@ -66,11 +59,6 @@ contract SuperchainConfig is Initializable, ISemver { guardian_ = Storage.getAddress(GUARDIAN_SLOT); } - /// @notice Getter for the upgrader address. - function upgrader() public view returns (address upgrader_) { - upgrader_ = Storage.getAddress(UPGRADER_SLOT); - } - /// @notice Getter for the current paused status. function paused() public view returns (bool paused_) { paused_ = Storage.getBool(PAUSED_SLOT); @@ -104,12 +92,4 @@ contract SuperchainConfig is Initializable, ISemver { Storage.setAddress(GUARDIAN_SLOT, _guardian); emit ConfigUpdate(UpdateType.GUARDIAN, abi.encode(_guardian)); } - - /// @notice Sets the upgrader address. This is only callable during initialization, so an upgrade - /// will be required to change the upgrader. - /// @param _upgrader The new upgrader address. - function _setUpgrader(address _upgrader) internal { - Storage.setAddress(UPGRADER_SLOT, _upgrader); - emit ConfigUpdate(UpdateType.UPGRADER, abi.encode(_upgrader)); - } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index f4b724b8d27e..afb9525403c7 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -4,18 +4,15 @@ pragma solidity 0.8.15; // Contracts import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { StaticConfig } from "src/libraries/StaticConfig.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; // Libraries import { Storage } from "src/libraries/Storage.sol"; import { Constants } from "src/libraries/Constants.sol"; import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IOptimismPortal2 as IOptimismPortal } from "src/L1/interfaces/IOptimismPortal2.sol"; +import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; /// @custom:proxied true @@ -51,18 +48,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { address gasPayingToken; } - /// @notice Struct representing the roles of the system. - /// @notice The owner (chain operator) of the system. - /// @notice The fee admin of the system. - /// @notice The unsafe block signer of the system. - /// @notice The batcher hash of the system. - struct Roles { - address owner; - address feeAdmin; - address unsafeBlockSigner; - bytes32 batcherHash; - } - /// @notice Version identifier, used for upgrades. uint256 public constant VERSION = 0; @@ -76,9 +61,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// but it is better to be safe than sorry. bytes32 public constant UNSAFE_BLOCK_SIGNER_SLOT = keccak256("systemconfig.unsafeblocksigner"); - /// @notice Storage slot that the feeAdmin address is stored at. - bytes32 internal constant FEE_ADMIN_SLOT = keccak256("systemconfig.feeadmin"); - /// @notice Storage slot that the L1CrossDomainMessenger address is stored at. bytes32 public constant L1_CROSS_DOMAIN_MESSENGER_SLOT = bytes32(uint256(keccak256("systemconfig.l1crossdomainmessenger")) - 1); @@ -155,9 +137,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.3.0-beta.6 + /// @custom:semver 2.3.0-beta.5 function version() public pure virtual returns (string memory) { - return "2.3.0-beta.6"; + return "2.3.0-beta.5"; } /// @notice Constructs the SystemConfig contract. Cannot set @@ -165,28 +147,55 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// implementation, so set it to `address(0xdEaD)` /// @dev START_BLOCK_SLOT is set to type(uint256).max here so that it will be a dead value /// in the singleton and is skipped by initialize when setting the start block. - /// _disableInitializers is called to prevent the need to make calls during the constructor - /// if initialize is called directly. constructor() { Storage.setUint(START_BLOCK_SLOT, type(uint256).max); - _disableInitializers(); + initialize({ + _owner: address(0xdEaD), + _basefeeScalar: 0, + _blobbasefeeScalar: 0, + _batcherHash: bytes32(0), + _gasLimit: 1, + _unsafeBlockSigner: address(0), + _config: IResourceMetering.ResourceConfig({ + maxResourceLimit: 1, + elasticityMultiplier: 1, + baseFeeMaxChangeDenominator: 2, + minimumBaseFee: 0, + systemTxMaxGas: 0, + maximumBaseFee: 0 + }), + _batchInbox: address(0), + _addresses: SystemConfig.Addresses({ + l1CrossDomainMessenger: address(0), + l1ERC721Bridge: address(0), + l1StandardBridge: address(0), + disputeGameFactory: address(0), + optimismPortal: address(0), + optimismMintableERC20Factory: address(0), + gasPayingToken: address(0) + }) + }); } /// @notice Initializer. /// The resource config must be set before the require check. - /// @param _roles Initial roles. + /// @param _owner Initial owner of the contract. /// @param _basefeeScalar Initial basefee scalar value. /// @param _blobbasefeeScalar Initial blobbasefee scalar value. + /// @param _batcherHash Initial batcher hash. /// @param _gasLimit Initial gas limit. + /// @param _unsafeBlockSigner Initial unsafe block signer address. /// @param _config Initial ResourceConfig. /// @param _batchInbox Batch inbox address. An identifier for the op-node to find /// canonical data. /// @param _addresses Set of L1 contract addresses. These should be the proxies. function initialize( - Roles memory _roles, + address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, + bytes32 _batcherHash, uint64 _gasLimit, + address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, SystemConfig.Addresses memory _addresses @@ -195,58 +204,29 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { initializer { __Ownable_init(); - transferOwnership(_roles.owner); + transferOwnership(_owner); // These are set in ascending order of their UpdateTypes. - _setBatcherHash(_roles.batcherHash); + _setBatcherHash(_batcherHash); _setGasConfigEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar }); _setGasLimit(_gasLimit); - Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _roles.unsafeBlockSigner); - Storage.setAddress(FEE_ADMIN_SLOT, _roles.feeAdmin); + Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner); Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); - - Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal); + Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT, _addresses.l1CrossDomainMessenger); + Storage.setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge); + Storage.setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge); Storage.setAddress(DISPUTE_GAME_FACTORY_SLOT, _addresses.disputeGameFactory); + Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal); Storage.setAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT, _addresses.optimismMintableERC20Factory); - _setGasPayingToken(_addresses.gasPayingToken); - _setAddress( - L1_CROSS_DOMAIN_MESSENGER_SLOT, - _addresses.l1CrossDomainMessenger, - Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS - ); - _setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge, Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); - _setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge, Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); - - _setRemoteChainId(); _setStartBlock(); - - // TODO: set fee vault config calls + _setGasPayingToken(_addresses.gasPayingToken); _setResourceConfig(_config); require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low"); } - /// @notice Internal setter for L1 system addresses that need to be legible from within L2. - /// @param _slot The local storage slot that the address should be stored in. - /// @param _addr The address of the L1 based system address. - /// @param _type The ConfigType that represents what the address is. - function _setAddress(bytes32 _slot, address _addr, Types.ConfigType _type) internal { - Storage.setAddress(_slot, _addr); - IOptimismPortal(payable(optimismPortal())).setConfig({ _type: _type, _value: abi.encode(_addr) }); - } - - /// @notice Internal setter for the base chain's chain id. This allows for the - /// base chain's chain id to be legible from within the parent chain. - /// In the case of an L2, this would be the L1 chain id. - function _setRemoteChainId() internal { - IOptimismPortal(payable(optimismPortal())).setConfig({ - _type: Types.ConfigType.REMOTE_CHAIN_ID, - _value: abi.encode(block.chainid) - }); - } - /// @notice Returns the minimum L2 gas limit that can be safely set for the system to /// operate. The L2 gas limit must be larger than or equal to the amount of /// gas that is allocated for deposits per block plus the amount of gas that @@ -273,12 +253,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { addr_ = Storage.getAddress(UNSAFE_BLOCK_SIGNER_SLOT); } - /// @notice High level getter for the fee admin address. - /// @return addr_ Address of the fee admin. - function feeAdmin() public view returns (address addr_) { - addr_ = Storage.getAddress(FEE_ADMIN_SLOT); - } - /// @notice Getter for the L1CrossDomainMessenger address. function l1CrossDomainMessenger() external view returns (address addr_) { addr_ = Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT); @@ -346,7 +320,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { /// to set the token address. This prevents the token address from being changed /// and makes it explicitly opt-in to use custom gas token. /// @param _token Address of the gas paying token. - function _setGasPayingToken(address _token) internal { + function _setGasPayingToken(address _token) internal virtual { if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) { require( ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token" @@ -354,17 +328,14 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { bytes32 name = GasPayingToken.sanitize(ERC20(_token).name()); bytes32 symbol = GasPayingToken.sanitize(ERC20(_token).symbol()); - // Set the gas paying token in storage and call the OptimismPortal. + // Set the gas paying token in storage and in the OptimismPortal. GasPayingToken.set({ _token: _token, _decimals: GAS_PAYING_TOKEN_DECIMALS, _name: name, _symbol: symbol }); - IOptimismPortal(payable(optimismPortal())).setConfig( - Types.ConfigType.GAS_PAYING_TOKEN, - StaticConfig.encodeSetGasPayingToken({ - _token: _token, - _decimals: GAS_PAYING_TOKEN_DECIMALS, - _name: name, - _symbol: symbol - }) - ); + IOptimismPortal(payable(optimismPortal())).setGasPayingToken({ + _token: _token, + _decimals: GAS_PAYING_TOKEN_DECIMALS, + _name: name, + _symbol: symbol + }); } } @@ -445,47 +416,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { _setGasLimit(_gasLimit); } - /// @notice Setter for the FeeVault predeploy configuration. - /// @param _type The FeeVault type. - /// @param _recipient Address that should receive the funds. - /// @param _min Minimum withdrawal amount allowed to be processed. - /// @param _network The network in which the fees should be withdrawn to. - function setFeeVaultConfig( - Types.ConfigType _type, - address _recipient, - uint256 _min, - Types.WithdrawalNetwork _network - ) - external - { - require(msg.sender == feeAdmin(), "SystemConfig: caller is not the fee admin"); - _setFeeVaultConfig(_type, _recipient, _min, _network); - } - - /// @notice Internal function for setting the FeeVault config by type. - /// @param _type The FeeVault type - /// @param _recipient Address that should receive the funds. - /// @param _min Minimum withdrawal amount allowed to be processed. - /// @param _network The network in which the fees should be withdrawn to. - function _setFeeVaultConfig( - Types.ConfigType _type, - address _recipient, - uint256 _min, - Types.WithdrawalNetwork _network - ) - internal - { - require( - _type == Types.ConfigType.BASE_FEE_VAULT_CONFIG || _type == Types.ConfigType.L1_FEE_VAULT_CONFIG - || _type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, - "SystemConfig: ConfigType is is not a Fee Vault Config type" - ); - IOptimismPortal(payable(optimismPortal())).setConfig({ - _type: _type, - _value: abi.encode(Encoding.encodeFeeVaultConfig(_recipient, _min, _network)) - }); - } - /// @notice Internal function for updating the L2 gas limit. /// @param _gasLimit New gas limit. function _setGasLimit(uint64 _gasLimit) internal { @@ -562,9 +492,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { "SystemConfig: precision loss with target resource limit" ); - // TODO: maxResourceLimit must be large enough to handle the SystemConfig.initialize - // call - _resourceConfig = _config; } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 755c279fc8ac..032109286596 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -2,13 +2,16 @@ pragma solidity 0.8.15; // Contracts +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IOptimismPortalInterop as IOptimismPortal } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; +import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries +import { Constants } from "src/libraries/Constants.sol"; +import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Storage } from "src/libraries/Storage.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -25,20 +28,24 @@ contract SystemConfigInterop is SystemConfig { 0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c; /// @notice Initializer. - /// @param _roles Initial roles. + /// @param _owner Initial owner of the contract. /// @param _basefeeScalar Initial basefee scalar value. /// @param _blobbasefeeScalar Initial blobbasefee scalar value. + /// @param _batcherHash Initial batcher hash. /// @param _gasLimit Initial gas limit. + /// @param _unsafeBlockSigner Initial unsafe block signer address. /// @param _config Initial ResourceConfig. /// @param _batchInbox Batch inbox address. An identifier for the op-node to find /// canonical data. /// @param _addresses Set of L1 contract addresses. These should be the proxies. /// @param _dependencyManager The addressed allowed to add/remove from the dependency set function initialize( - SystemConfig.Roles memory _roles, + address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, + bytes32 _batcherHash, uint64 _gasLimit, + address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, SystemConfig.Addresses memory _addresses, @@ -48,10 +55,12 @@ contract SystemConfigInterop is SystemConfig { { // This method has an initializer modifier, and will revert if already initialized. initialize({ - _roles: _roles, + _owner: _owner, _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar, + _batcherHash: _batcherHash, _gasLimit: _gasLimit, + _unsafeBlockSigner: _unsafeBlockSigner, _config: _config, _batchInbox: _batchInbox, _addresses: _addresses @@ -59,9 +68,38 @@ contract SystemConfigInterop is SystemConfig { Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } - /// @custom:semver +interop-beta.4 + /// @custom:semver +interop-beta.3 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.4"); + return string.concat(super.version(), "+interop-beta.3"); + } + + /// @notice Internal setter for the gas paying token address, includes validation. + /// The token must not already be set and must be non zero and not the ether address + /// to set the token address. This prevents the token address from being changed + /// and makes it explicitly opt-in to use custom gas token. Additionally, + /// OptimismPortal's address must be non zero, since otherwise the call to set the + /// config for the gas paying token to OptimismPortal will fail. + /// @param _token Address of the gas paying token. + function _setGasPayingToken(address _token) internal override { + if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) { + require( + ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token" + ); + bytes32 name = GasPayingToken.sanitize(ERC20(_token).name()); + bytes32 symbol = GasPayingToken.sanitize(ERC20(_token).symbol()); + + // Set the gas paying token in storage and in the OptimismPortal. + GasPayingToken.set({ _token: _token, _decimals: GAS_PAYING_TOKEN_DECIMALS, _name: name, _symbol: symbol }); + IOptimismPortal(payable(optimismPortal())).setConfig( + ConfigType.SET_GAS_PAYING_TOKEN, + StaticConfig.encodeSetGasPayingToken({ + _token: _token, + _decimals: GAS_PAYING_TOKEN_DECIMALS, + _name: name, + _symbol: symbol + }) + ); + } } /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. @@ -69,7 +107,7 @@ contract SystemConfigInterop is SystemConfig { function addDependency(uint256 _chainId) external { require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); IOptimismPortal(payable(optimismPortal())).setConfig( - Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId) + ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId) ); } @@ -78,7 +116,7 @@ contract SystemConfigInterop is SystemConfig { function removeDependency(uint256 _chainId) external { require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); IOptimismPortal(payable(optimismPortal())).setConfig( - Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId) + ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId) ); } diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol index eb3d0250af2f..8a6de84e2c9d 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessenger.sol @@ -7,8 +7,6 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; interface IL1CrossDomainMessenger is ICrossDomainMessenger { - event Initialized(uint8 version); - function PORTAL() external view returns (IOptimismPortal); function initialize( ISuperchainConfig _superchainConfig, @@ -16,8 +14,6 @@ interface IL1CrossDomainMessenger is ICrossDomainMessenger { ISystemConfig _systemConfig ) external; - function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); - function otherMessenger() external pure returns (ICrossDomainMessenger); function portal() external view returns (IOptimismPortal); function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol index ec111614a523..51356bc8d346 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1ERC721Bridge.sol @@ -6,11 +6,6 @@ import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMess import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; interface IL1ERC721Bridge is IERC721Bridge { - error InvalidInitialization(); - error NotInitializing(); - - event Initialized(uint64 version); - function bridgeERC721( address _localToken, address _remoteToken, @@ -39,7 +34,6 @@ interface IL1ERC721Bridge is IERC721Bridge { ) external; function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) external; - function otherBridge() external pure returns (IERC721Bridge); function paused() external view returns (bool); function superchainConfig() external view returns (ISuperchainConfig); function version() external view returns (string memory); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol deleted file mode 100644 index 7ac1d3175d40..000000000000 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1OptimismMintableERC20Factory.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IL1OptimismMintableERC20Factory { - event Initialized(uint8 version); - event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); - event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); - - function BRIDGE() external view returns (address); - function bridge() external view returns (address); - function createOptimismMintableERC20( - address _remoteToken, - string memory _name, - string memory _symbol - ) - external - returns (address); - function createOptimismMintableERC20WithDecimals( - address _remoteToken, - string memory _name, - string memory _symbol, - uint8 _decimals - ) - external - returns (address); - function createStandardL2Token( - address _remoteToken, - string memory _name, - string memory _symbol - ) - external - returns (address); - function deployments(address) external view returns (address); - function initialize(address _bridge) external; - function version() external view returns (string memory); - - function __constructor__() external; -} diff --git a/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol b/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol index 53419cdd59d5..816436cf1084 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridge.sol @@ -25,7 +25,6 @@ interface IL1StandardBridge is IStandardBridge { ); event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); - event Initialized(uint8 version); function depositERC20( address _l1Token, @@ -69,9 +68,7 @@ interface IL1StandardBridge is IStandardBridge { ISystemConfig _systemConfig ) external; - function l2TokenBridge() external pure returns (address); - function OTHER_BRIDGE() external view returns (IStandardBridge); - function otherBridge() external pure returns (IStandardBridge); + function l2TokenBridge() external view returns (address); function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); function version() external view returns (string memory); diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol index 69f9fb9e16c6..b9035a6e5143 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal.sol @@ -5,7 +5,6 @@ import { Types } from "src/libraries/Types.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol"; -import { Types } from "src/libraries/Types.sol"; interface IOptimismPortal { error BadTarget(); @@ -79,9 +78,9 @@ interface IOptimismPortal { external view returns (bytes32 outputRoot, uint128 timestamp, uint128 l2OutputIndex); // nosemgrep + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); - function setConfig(Types.ConfigType _type, bytes memory _value) external; function version() external pure returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol index 51395720f283..91f09d714314 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortal2.sol @@ -7,7 +7,6 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -import { Types } from "src/libraries/Types.sol"; interface IOptimismPortal2 { error AlreadyFinalized(); @@ -109,12 +108,11 @@ interface IOptimismPortal2 { returns (IDisputeGame disputeGameProxy, uint64 timestamp); // nosemgrep function respectedGameType() external view returns (GameType); function respectedGameTypeUpdatedAt() external view returns (uint64); + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; function setRespectedGameType(GameType _gameType) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); function version() external pure returns (string memory); - function setConfig(Types.ConfigType _type, bytes memory _value) external; - function upgrade(uint32 _gasLimit, bytes memory _calldata) external; function __constructor__(uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds) external; } diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol index 7a66caecbdb7..521c7232e125 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol @@ -7,6 +7,7 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { ConfigType } from "src/L2/L1BlockInterop.sol"; interface IOptimismPortalInterop { error AlreadyFinalized(); @@ -108,11 +109,11 @@ interface IOptimismPortalInterop { returns (IDisputeGame disputeGameProxy, uint64 timestamp); // nosemgrep function respectedGameType() external view returns (GameType); function respectedGameTypeUpdatedAt() external view returns (uint64); - function setConfig(Types.ConfigType _type, bytes memory _value) external; + function setConfig(ConfigType _type, bytes memory _value) external; + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; function setRespectedGameType(GameType _gameType) external; function superchainConfig() external view returns (ISuperchainConfig); function systemConfig() external view returns (ISystemConfig); - function upgrade(uint32 _gasLimit, bytes memory _calldata) external; function version() external pure returns (string memory); function __constructor__(uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds) external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol b/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol index 88f9a7cffae9..dc83893958b0 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISuperchainConfig.sol @@ -13,10 +13,8 @@ interface ISuperchainConfig { function GUARDIAN_SLOT() external view returns (bytes32); function PAUSED_SLOT() external view returns (bytes32); - function UPGRADER_SLOT() external view returns (bytes32); function guardian() external view returns (address guardian_); - function upgrader() external view returns (address upgrader_); - function initialize(address _guardian, address _upgrader, bool _paused) external; + function initialize(address _guardian, bool _paused) external; function pause(string memory _identifier) external; function paused() external view returns (bool paused_); function unpause() external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol index 5b68bd7fd099..8959c00b744a 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfig.sol @@ -2,12 +2,9 @@ pragma solidity ^0.8.0; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; -import { Types } from "src/libraries/Types.sol"; /// @notice This interface corresponds to the Custom Gas Token version of the SystemConfig contract. interface ISystemConfig { - error UnsafeCast(); - enum UpdateType { BATCHER, FEE_SCALARS, @@ -26,13 +23,6 @@ interface ISystemConfig { address gasPayingToken; } - struct Roles { - address owner; - address feeAdmin; - address unsafeBlockSigner; - bytes32 batcherHash; - } - event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); event Initialized(uint8 version); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); @@ -59,10 +49,12 @@ interface ISystemConfig { function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); function initialize( - Roles memory _roles, + address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, + bytes32 _batcherHash, uint64 _gasLimit, + address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, Addresses memory _addresses @@ -78,18 +70,10 @@ interface ISystemConfig { function optimismPortal() external view returns (address addr_); function overhead() external view returns (uint256); function owner() external view returns (address); - function feeAdmin() external view returns (address addr_); function renounceOwnership() external; function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); function scalar() external view returns (uint256); function setBatcherHash(bytes32 _batcherHash) external; - function setFeeVaultConfig( - Types.ConfigType _type, - address _recipient, - uint256 _min, - Types.WithdrawalNetwork _network - ) - external; function setGasConfig(uint256 _overhead, uint256 _scalar) external; function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external; function setGasLimit(uint64 _gasLimit) external; diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol index 65eccead56e7..e11d17c9f7bd 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigInterop.sol @@ -40,7 +40,6 @@ interface ISystemConfigInterop { function optimismPortal() external view returns (address addr_); function overhead() external view returns (uint256); function owner() external view returns (address); - function feeAdmin() external view returns (address); function renounceOwnership() external; function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); function scalar() external view returns (uint256); @@ -58,10 +57,12 @@ interface ISystemConfigInterop { function removeDependency(uint256 _chainId) external; function dependencyManager() external view returns (address); function initialize( - ISystemConfig.Roles memory _roles, + address _owner, uint32 _basefeeScalar, uint32 _blobbasefeeScalar, + bytes32 _batcherHash, uint64 _gasLimit, + address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, ISystemConfig.Addresses memory _addresses, diff --git a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol index 29710b9eece6..2fd33b9290bf 100644 --- a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; + import { Types } from "src/libraries/Types.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000019 @@ -12,20 +12,18 @@ import { Encoding } from "src/libraries/Encoding.sol"; /// @notice The BaseFeeVault accumulates the base fee that is paid by transactions. contract BaseFeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.4 - string public constant version = "1.5.0-beta.4"; + /// @custom:semver 1.5.0-beta.3 + string public constant version = "1.5.0-beta.3"; - /// @notice Returns the FeeVault config - /// @return recipient_ Wallet that will receive the fees. - /// @return amount_ Minimum balance for withdrawals. - /// @return withdrawalNetwork_ Network which the recipient will receive fees on. - function config() - public - view - override - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) - { - bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG); - (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); - } + /// @notice Constructs the BaseFeeVault contract. + /// @param _recipient Wallet that will receive the fees. + /// @param _minWithdrawalAmount Minimum balance for withdrawals. + /// @param _withdrawalNetwork Network which the recipient will receive fees on. + constructor( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) + { } } diff --git a/packages/contracts-bedrock/src/L2/FeeVault.sol b/packages/contracts-bedrock/src/L2/FeeVault.sol index 3d2ecfd6e1a3..856985d7827b 100644 --- a/packages/contracts-bedrock/src/L2/FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/FeeVault.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.15; // Libraries import { SafeCall } from "src/libraries/SafeCall.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; // Interfaces import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; @@ -16,15 +15,27 @@ import { Types } from "src/libraries/Types.sol"; /// @notice The FeeVault contract contains the basic logic for the various different vault contracts /// used to hold fee revenue generated by the L2 system. abstract contract FeeVault { + /// @notice Minimum balance before a withdrawal can be triggered. + /// Use the `minWithdrawalAmount()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy + uint256 public immutable MIN_WITHDRAWAL_AMOUNT; + + /// @notice Account that will receive the fees. Can be located on L1 or L2. + /// Use the `recipient()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy + address public immutable RECIPIENT; + + /// @notice Network which the recipient will receive fees on. + /// Use the `withdrawalNetwork()` getter as this is deprecated + /// and is subject to be removed in the future. + /// @custom:legacy + Types.WithdrawalNetwork public immutable WITHDRAWAL_NETWORK; + /// @notice The minimum gas limit for the FeeVault withdrawal transaction. uint32 internal constant WITHDRAWAL_MIN_GAS = 400_000; - /// @notice Internal getter function for the L1Block contract. - /// @return Contract of the L1Block on this domain. - function L1_BLOCK() internal pure returns (IL1Block) { - return IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES); - } - /// @notice Total amount of wei processed by the contract. uint256 public totalProcessed; @@ -45,76 +56,52 @@ abstract contract FeeVault { /// @param withdrawalNetwork Network which the to address will receive funds on. event Withdrawal(uint256 value, address to, address from, Types.WithdrawalNetwork withdrawalNetwork); + /// @param _recipient Wallet that will receive the fees. + /// @param _minWithdrawalAmount Minimum balance for withdrawals. + /// @param _withdrawalNetwork Network which the recipient will receive fees on. + constructor(address _recipient, uint256 _minWithdrawalAmount, Types.WithdrawalNetwork _withdrawalNetwork) { + RECIPIENT = _recipient; + MIN_WITHDRAWAL_AMOUNT = _minWithdrawalAmount; + WITHDRAWAL_NETWORK = _withdrawalNetwork; + } + /// @notice Allow the contract to receive ETH. receive() external payable { } - /// @notice Returns the configuration of the FeeVault. - function config() - public - view - virtual - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); - /// @notice Minimum balance before a withdrawal can be triggered. - function minWithdrawalAmount() public view virtual returns (uint256 amount_) { - (, amount_,) = config(); - } - - /// @notice Minimum balance before a withdrawal can be triggered. - /// Use the `minWithdrawalAmount()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy true - function MIN_WITHDRAWAL_AMOUNT() public view returns (uint256) { - return minWithdrawalAmount(); + function minWithdrawalAmount() public view returns (uint256 amount_) { + amount_ = MIN_WITHDRAWAL_AMOUNT; } /// @notice Account that will receive the fees. Can be located on L1 or L2. - function recipient() public view virtual returns (address recipient_) { - (recipient_,,) = config(); - } - - /// @notice Account that will receive the fees. Can be located on L1 or L2. - /// Use the `recipient()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy - function RECIPIENT() public view returns (address) { - return recipient(); + function recipient() public view returns (address recipient_) { + recipient_ = RECIPIENT; } /// @notice Network which the recipient will receive fees on. - function withdrawalNetwork() public view returns (Types.WithdrawalNetwork withdrawalNetwork_) { - (,, withdrawalNetwork_) = config(); - } - - /// @notice Network which the recipient will receive fees on. - /// Use the `withdrawalNetwork()` getter as this is deprecated - /// and is subject to be removed in the future. - /// @custom:legacy - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_) { - withdrawalNetwork_ = withdrawalNetwork(); + function withdrawalNetwork() public view returns (Types.WithdrawalNetwork network_) { + network_ = WITHDRAWAL_NETWORK; } /// @notice Triggers a withdrawal of funds to the fee wallet on L1 or L2. function withdraw() external { - (address withdrawalRecipient, uint256 withdrawalAmount, Types.WithdrawalNetwork network) = config(); - require( - address(this).balance >= withdrawalAmount, + address(this).balance >= MIN_WITHDRAWAL_AMOUNT, "FeeVault: withdrawal amount must be greater than minimum withdrawal amount" ); uint256 value = address(this).balance; totalProcessed += value; - emit Withdrawal(value, withdrawalRecipient, msg.sender); - emit Withdrawal(value, withdrawalRecipient, msg.sender, network); + emit Withdrawal(value, RECIPIENT, msg.sender); + emit Withdrawal(value, RECIPIENT, msg.sender, WITHDRAWAL_NETWORK); - if (network == Types.WithdrawalNetwork.L2) { - bool success = SafeCall.send(withdrawalRecipient, value); + if (WITHDRAWAL_NETWORK == Types.WithdrawalNetwork.L2) { + bool success = SafeCall.send(RECIPIENT, value); require(success, "FeeVault: failed to send ETH to L2 fee recipient"); } else { IL2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)).initiateWithdrawal{ value: value }({ - _target: withdrawalRecipient, + _target: RECIPIENT, _gasLimit: WITHDRAWAL_MIN_GAS, _data: hex"" }); diff --git a/packages/contracts-bedrock/src/L2/GasPriceOracle.sol b/packages/contracts-bedrock/src/L2/GasPriceOracle.sol index 8b3b71f7c19f..45f14fe173d0 100644 --- a/packages/contracts-bedrock/src/L2/GasPriceOracle.sol +++ b/packages/contracts-bedrock/src/L2/GasPriceOracle.sol @@ -29,8 +29,8 @@ contract GasPriceOracle is ISemver { uint256 public constant DECIMALS = 6; /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.4 - string public constant version = "1.3.1-beta.4"; + /// @custom:semver 1.3.1-beta.3 + string public constant version = "1.3.1-beta.3"; /// @notice This is the intercept value for the linear regression used to estimate the final size of the /// compressed transaction. diff --git a/packages/contracts-bedrock/src/L2/L1Block.sol b/packages/contracts-bedrock/src/L2/L1Block.sol index 5a1646bca285..0b2ffd2c5782 100644 --- a/packages/contracts-bedrock/src/L2/L1Block.sol +++ b/packages/contracts-bedrock/src/L2/L1Block.sol @@ -3,17 +3,7 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol"; -import { IFeeVault, Types as ITypes } from "src/L2/interfaces/IFeeVault.sol"; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; -import { IERC721Bridge } from "src/universal/interfaces/IERC721Bridge.sol"; -import { IOptimismMintableERC721Factory } from "src/L2/interfaces/IOptimismMintableERC721Factory.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; -import { Storage } from "src/libraries/Storage.sol"; -import { Types } from "src/libraries/Types.sol"; import { NotDepositor } from "src/libraries/L1BlockErrors.sol"; /// @custom:proxied true @@ -27,31 +17,6 @@ contract L1Block is ISemver, IGasToken { /// @notice Event emitted when the gas paying token is set. event GasPayingTokenSet(address indexed token, uint8 indexed decimals, bytes32 name, bytes32 symbol); - /// @notice Storage slot for the base fee vault configuration - bytes32 internal constant BASE_FEE_VAULT_CONFIG_SLOT = bytes32(uint256(keccak256("opstack.basefeevaultconfig")) - 1); - - /// @notice Storage slot for the L1 fee vault configuration - bytes32 internal constant L1_FEE_VAULT_CONFIG_SLOT = bytes32(uint256(keccak256("opstack.l1feevaultconfig")) - 1); - - /// @notice Storage slot for the sequencer fee vault configuration - bytes32 internal constant SEQUENCER_FEE_VAULT_CONFIG_SLOT = - bytes32(uint256(keccak256("opstack.sequencerfeevaultconfig")) - 1); - - /// @notice Storage slot for the L1 cross domain messenger address - bytes32 internal constant L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT = - bytes32(uint256(keccak256("opstack.l1crossdomainmessengeraddress")) - 1); - - /// @notice Storage slot for the L1 ERC721 bridge address - bytes32 internal constant L1_ERC_721_BRIDGE_ADDRESS_SLOT = - bytes32(uint256(keccak256("opstack.l1erc721bridgeaddress")) - 1); - - /// @notice Storage slot for the L1 standard bridge address - bytes32 internal constant L1_STANDARD_BRIDGE_ADDRESS_SLOT = - bytes32(uint256(keccak256("opstack.l1standardbridgeaddress")) - 1); - - /// @notice Storage slot for the remote chain ID - bytes32 internal constant REMOTE_CHAIN_ID_SLOT = bytes32(uint256(keccak256("opstack.remotechainid")) - 1); - /// @notice Address of the special depositor account. function DEPOSITOR_ACCOUNT() public pure returns (address addr_) { addr_ = Constants.DEPOSITOR_ACCOUNT; @@ -92,12 +57,9 @@ contract L1Block is ISemver, IGasToken { /// @notice The latest L1 blob base fee. uint256 public blobBaseFee; - /// @notice Indicates whether the network has gone through the Isthmus upgrade. - bool public isIsthmus; - - /// @custom:semver 1.5.1-beta.4 + /// @custom:semver 1.5.1-beta.3 function version() public pure virtual returns (string memory) { - return "1.5.1-beta.4"; + return "1.5.1-beta.3"; } /// @notice Returns the gas paying token, its decimals, name and symbol. @@ -206,117 +168,14 @@ contract L1Block is ISemver, IGasToken { } } - /// @notice Sets static configuration options for the L2 system. Can only be called by the special - /// depositor account. - /// @param _type The type of configuration to set. - /// @param _value The encoded value with which to set the configuration. - function setConfig(Types.ConfigType _type, bytes calldata _value) public virtual { - if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); - - if (_type == Types.ConfigType.GAS_PAYING_TOKEN) { - (address token, uint8 decimals, bytes32 name, bytes32 symbol) = StaticConfig.decodeSetGasPayingToken(_value); - GasPayingToken.set({ _token: token, _decimals: decimals, _name: name, _symbol: symbol }); - emit GasPayingTokenSet({ token: token, decimals: decimals, name: name, symbol: symbol }); - } else if (_type == Types.ConfigType.BASE_FEE_VAULT_CONFIG) { - Storage.setBytes32(BASE_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); - } else if (_type == Types.ConfigType.L1_FEE_VAULT_CONFIG) { - Storage.setBytes32(L1_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); - } else if (_type == Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS) { - Storage.setAddress(L1_ERC_721_BRIDGE_ADDRESS_SLOT, abi.decode(_value, (address))); - } else if (_type == Types.ConfigType.REMOTE_CHAIN_ID) { - Storage.setUint(REMOTE_CHAIN_ID_SLOT, abi.decode(_value, (uint256))); - } else if (_type == Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS) { - Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT, abi.decode(_value, (address))); - } else if (_type == Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS) { - Storage.setAddress(L1_STANDARD_BRIDGE_ADDRESS_SLOT, abi.decode(_value, (address))); - } else if (_type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG) { - Storage.setBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT, abi.decode(_value, (bytes32))); - } - } - - /// @notice Returns the configuration value for the given type. All configuration related storage values in - /// the L2 contracts, should be stored in this contract. - /// @param _type The type of configuration to get. - /// @return data_ The encoded configuration value. - function getConfig(Types.ConfigType _type) public view virtual returns (bytes memory data_) { - if (_type == Types.ConfigType.GAS_PAYING_TOKEN) { - (address addr, uint8 decimals) = gasPayingToken(); - data_ = abi.encode( - addr, - decimals, - GasPayingToken.sanitize(gasPayingTokenName()), - GasPayingToken.sanitize(gasPayingTokenSymbol()) - ); - } else if (_type == Types.ConfigType.BASE_FEE_VAULT_CONFIG) { - data_ = abi.encode(Storage.getBytes32(BASE_FEE_VAULT_CONFIG_SLOT)); - } else if (_type == Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS) { - data_ = abi.encode(Storage.getAddress(L1_ERC_721_BRIDGE_ADDRESS_SLOT)); - } else if (_type == Types.ConfigType.REMOTE_CHAIN_ID) { - data_ = abi.encode(Storage.getUint(REMOTE_CHAIN_ID_SLOT)); - } else if (_type == Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS) { - data_ = abi.encode(Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT)); - } else if (_type == Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS) { - data_ = abi.encode(Storage.getAddress(L1_STANDARD_BRIDGE_ADDRESS_SLOT)); - } else if (_type == Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG) { - data_ = abi.encode(Storage.getBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT)); - } else if (_type == Types.ConfigType.L1_FEE_VAULT_CONFIG) { - data_ = abi.encode(Storage.getBytes32(L1_FEE_VAULT_CONFIG_SLOT)); - } - } - /// @notice Sets the gas paying token for the L2 system. Can only be called by the special - /// depositor account, initiated by a deposit transaction from L1. - /// This is a legacy setter that exists to give compabilitity with the legacy SystemConfig. - /// Custom gas token can now be set using `setConfig`. This can be removed in the future. + /// depositor account. This function is not called on every L2 block but instead + /// only called by specially crafted L1 deposit transactions. function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); GasPayingToken.set({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); - emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); - } - /// @notice Sets the L1 block values for an Isthmus upgraded chain. - /// This function is intended to be called only once, and only on existing chains which are undergoing - /// the Isthmus upgrade. Chains deployed with the Isthmus upgrade activated will have the values set here - /// already populated by the L2 Genesis generation process. - /// In the case of an existing chain underoing the Isthmus upgrade, the expectation is that - /// The upgrade flow will use the following series of Network upgrade automation transactions: - /// 1. Deploy a new `L1BlockImpl` contract. - /// 2. Upgrade only the `L1Block` contract to the new implementation by - /// calling `L2ProxyAdmin.upgrade(address(L1BlockProxy), address(L1BlockImpl))`. - /// 3. Call `L1Block.setIsthmus()` to pull the values from L2 contracts. - /// 4. Upgrades the remainder of the L2 contracts via `L2ProxyAdmin.upgrade()`. - function setIsthmus() external { - if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); - require(isIsthmus == false, "L1Block: Isthmus already active"); - isIsthmus = true; - - Storage.setBytes32(BASE_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.BASE_FEE_VAULT)); - Storage.setBytes32(L1_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.L1_FEE_VAULT)); - Storage.setBytes32(SEQUENCER_FEE_VAULT_CONFIG_SLOT, _migrateFeeVaultConfig(Predeploys.SEQUENCER_FEE_WALLET)); - - Storage.setAddress( - L1_CROSS_DOMAIN_MESSENGER_ADDRESS_SLOT, - address(ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).otherMessenger()) - ); - Storage.setAddress( - L1_STANDARD_BRIDGE_ADDRESS_SLOT, - address(IStandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).otherBridge()) - ); - Storage.setAddress( - L1_ERC_721_BRIDGE_ADDRESS_SLOT, address(IERC721Bridge(Predeploys.L2_ERC721_BRIDGE).otherBridge()) - ); - Storage.setUint( - REMOTE_CHAIN_ID_SLOT, - IOptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY).remoteChainId() - ); - } - - /// @notice Helper function for migrating deploy config. - function _migrateFeeVaultConfig(address _addr) internal view returns (bytes32) { - address recipient = IFeeVault(payable(_addr)).recipient(); - uint256 amount = IFeeVault(payable(_addr)).minWithdrawalAmount(); - ITypes.WithdrawalNetwork network = IFeeVault(payable(_addr)).withdrawalNetwork(); - return Encoding.encodeFeeVaultConfig(recipient, amount, Types.WithdrawalNetwork(uint8(network))); + emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); } } diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 650799383528..2cf6bd96c7d4 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -6,9 +6,9 @@ import { L1Block } from "src/L2/L1Block.sol"; // Libraries import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; import { NotDepositor, NotCrossL2Inbox, @@ -18,6 +18,16 @@ import { CantRemovedDependency } from "src/libraries/L1BlockErrors.sol"; +/// @notice Enum representing different types of configurations that can be set on L1BlockInterop. +/// @custom:value SET_GAS_PAYING_TOKEN Represents the config type for setting the gas paying token. +/// @custom:value ADD_DEPENDENCY Represents the config type for adding a chain to the interop dependency set. +/// @custom:value REMOVE_DEPENDENCY Represents the config type for removing a chain from the interop dependency set. +enum ConfigType { + SET_GAS_PAYING_TOKEN, + ADD_DEPENDENCY, + REMOVE_DEPENDENCY +} + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1BlockInterop @@ -39,9 +49,9 @@ contract L1BlockInterop is L1Block { /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300; - /// @custom:semver +interop-beta.2 + /// @custom:semver +interop-beta.1 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.2"); + return string.concat(super.version(), "+interop-beta.1"); } /// @notice Returns whether the call was triggered from a a deposit or not. @@ -94,16 +104,28 @@ contract L1BlockInterop is L1Block { /// depositor account. /// @param _type The type of configuration to set. /// @param _value The encoded value with which to set the configuration. - function setConfig(Types.ConfigType _type, bytes calldata _value) public override { - super.setConfig(_type, _value); + function setConfig(ConfigType _type, bytes calldata _value) external { + if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); - if (_type == Types.ConfigType.ADD_DEPENDENCY) { + if (_type == ConfigType.SET_GAS_PAYING_TOKEN) { + _setGasPayingToken(_value); + } else if (_type == ConfigType.ADD_DEPENDENCY) { _addDependency(_value); - } else if (_type == Types.ConfigType.REMOVE_DEPENDENCY) { + } else if (_type == ConfigType.REMOVE_DEPENDENCY) { _removeDependency(_value); } } + /// @notice Internal method to set the gas paying token. + /// @param _value The encoded value with which to set the gas paying token. + function _setGasPayingToken(bytes calldata _value) internal { + (address token, uint8 decimals, bytes32 name, bytes32 symbol) = StaticConfig.decodeSetGasPayingToken(_value); + + GasPayingToken.set({ _token: token, _decimals: decimals, _name: name, _symbol: symbol }); + + emit GasPayingTokenSet({ token: token, decimals: decimals, name: name, symbol: symbol }); + } + /// @notice Internal method to add a dependency to the interop dependency set. /// @param _value The encoded value with which to add the dependency. function _addDependency(bytes calldata _value) internal { diff --git a/packages/contracts-bedrock/src/L2/L1FeeVault.sol b/packages/contracts-bedrock/src/L2/L1FeeVault.sol index 62c04faf3024..c80c40b98493 100644 --- a/packages/contracts-bedrock/src/L2/L1FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/L1FeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; + import { Types } from "src/libraries/Types.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x420000000000000000000000000000000000001A @@ -12,20 +12,18 @@ import { Encoding } from "src/libraries/Encoding.sol"; /// @notice The L1FeeVault accumulates the L1 portion of the transaction fees. contract L1FeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.4 - string public constant version = "1.5.0-beta.4"; + /// @custom:semver 1.5.0-beta.3 + string public constant version = "1.5.0-beta.3"; - /// @notice Returns the FeeVault config - /// @return recipient_ Wallet that will receive the fees. - /// @return amount_ Minimum balance for withdrawals. - /// @return withdrawalNetwork_ Network which the recipient will receive fees on. - function config() - public - view - override - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) - { - bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG); - (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); - } + /// @notice Constructs the L1FeeVault contract. + /// @param _recipient Wallet that will receive the fees. + /// @param _minWithdrawalAmount Minimum balance for withdrawals. + /// @param _withdrawalNetwork Network which the recipient will receive fees on. + constructor( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) + { } } diff --git a/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol index d4b19e35f97b..2461e46d2cf5 100644 --- a/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol @@ -7,7 +7,6 @@ import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; // Libraries import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -21,22 +20,26 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// L2 on the L2 side. Users are generally encouraged to use this contract instead of lower /// level message passing contracts. contract L2CrossDomainMessenger is CrossDomainMessenger, ISemver { - /// @custom:semver 2.1.1-beta.5 - string public constant version = "2.1.1-beta.5"; - - /// @notice Getter for the remote chain's messenger. - function otherMessenger() public view override returns (CrossDomainMessenger) { - bytes memory data = - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS); - return CrossDomainMessenger(abi.decode(data, (address))); + /// @custom:semver 2.1.1-beta.4 + string public constant version = "2.1.1-beta.4"; + + /// @notice Constructs the L2CrossDomainMessenger contract. + constructor() CrossDomainMessenger() { + initialize({ _l1CrossDomainMessenger: CrossDomainMessenger(address(0)) }); + } + + /// @notice Initializer. + /// @param _l1CrossDomainMessenger L1CrossDomainMessenger contract on the other network. + function initialize(CrossDomainMessenger _l1CrossDomainMessenger) public initializer { + __CrossDomainMessenger_init({ _otherMessenger: _l1CrossDomainMessenger }); } - /// @notice Legay getter for the remote chain's messenger. + /// @notice Getter for the remote messenger. /// Public getter is legacy and will be removed in the future. Use `otherMessenger()` instead. /// @return L1CrossDomainMessenger contract. /// @custom:legacy function l1CrossDomainMessenger() public view returns (CrossDomainMessenger) { - return otherMessenger(); + return otherMessenger; } /// @inheritdoc CrossDomainMessenger @@ -53,7 +56,7 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, ISemver { /// @inheritdoc CrossDomainMessenger function _isOtherMessenger() internal view override returns (bool) { - return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == address(otherMessenger()); + return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == address(otherMessenger); } /// @inheritdoc CrossDomainMessenger diff --git a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol index 9536696491ba..85c6856e629d 100644 --- a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.25; +pragma solidity 0.8.15; // Contracts import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; @@ -7,13 +7,11 @@ import { ERC721Bridge } from "src/universal/ERC721Bridge.sol"; // Libraries import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:proxied true @@ -28,21 +26,21 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// wait for the one-week challenge period to elapse before their Optimism-native NFT /// can be refunded on L2. contract L2ERC721Bridge is ERC721Bridge, ISemver { - /// @custom:semver 1.8.0-beta.3 - string public constant version = "1.8.0-beta.3"; + /// @custom:semver 1.8.0-beta.2 + string public constant version = "1.8.0-beta.2"; - /// @notice Getter function for the messenger contract. - /// @return Address of the messenger on this domain. - function messenger() public pure override returns (ICrossDomainMessenger) { - return ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + /// @notice Constructs the L2ERC721Bridge contract. + constructor() ERC721Bridge() { + initialize({ _l1ERC721Bridge: payable(address(0)) }); } - /// @notice Getter function for the other bridge. - /// @return Address of the bridge on the other network. - function otherBridge() public view override returns (ERC721Bridge) { - bytes memory data = - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); - return ERC721Bridge(abi.decode(data, (address))); + /// @notice Initializes the contract. + /// @param _l1ERC721Bridge Address of the ERC721 bridge contract on the other network. + function initialize(address payable _l1ERC721Bridge) public initializer { + __ERC721Bridge_init({ + _messenger: ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER), + _otherBridge: ERC721Bridge(_l1ERC721Bridge) + }); } /// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the @@ -125,7 +123,7 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver { // Send message to L1 bridge // slither-disable-next-line reentrancy-events - messenger().sendMessage({ _target: address(otherBridge()), _message: message, _minGasLimit: _minGasLimit }); + messenger.sendMessage({ _target: address(otherBridge), _message: message, _minGasLimit: _minGasLimit }); // slither-disable-next-line reentrancy-events emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData); diff --git a/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol deleted file mode 100644 index dbec47a6d246..000000000000 --- a/packages/contracts-bedrock/src/L2/L2OptimismMintableERC20Factory.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; - -/// @custom:proxied true -/// @custom:predeployed 0x4200000000000000000000000000000000000012 -/// @title L2OptimismMintableERC20Factory -/// @notice L2OptimismMintableERC20Factory is a factory contract that generates OptimismMintableERC20 -/// contracts on the network it's deployed to. Simplifies the deployment process for users -/// who may be less familiar with deploying smart contracts. Designed to be backwards -/// compatible with the older StandardL2ERC20Factory contract. -contract L2OptimismMintableERC20Factory is OptimismMintableERC20Factory { - /// @custom:semver 1.3.1-beta.5 - /// @notice Semantic version. - /// The semver MUST be bumped any time that there is a change in - /// the OptimismMintableERC20 token contract since this contract - /// is responsible for deploying OptimismMintableERC20 contracts. - string public constant version = "1.3.1-beta.5"; - - function bridge() public view virtual override returns (address) { - return Predeploys.L2_STANDARD_BRIDGE; - } -} diff --git a/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol b/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol deleted file mode 100644 index 38178fd51dd2..000000000000 --- a/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; -import { Constants } from "src/libraries/Constants.sol"; - -/// @custom:proxied true -/// @custom:predeploy -/// @title L2ProxyAdmin -contract L2ProxyAdmin is ProxyAdmin { - constructor() ProxyAdmin(Constants.DEPOSITOR_ACCOUNT) { } - - /// @notice The owner of the L2ProxyAdmin is the `DEPOSITOR_ACCOUNT`. - function owner() public pure override returns (address) { - return Constants.DEPOSITOR_ACCOUNT; - } -} diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol index 81b9f825f36a..63bda3209fbb 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol @@ -3,12 +3,10 @@ pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; @@ -60,21 +58,23 @@ contract L2StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.11.1-beta.4 + /// @custom:semver 1.11.1-beta.3 function version() public pure virtual returns (string memory) { - return "1.11.1-beta.4"; + return "1.11.1-beta.3"; } - /// @notice Returns the corresponding L1 StandardBridge contract. - function otherBridge() public view override returns (IStandardBridge) { - bytes memory data = - IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); - return IStandardBridge(abi.decode(data, (address))); + /// @notice Constructs the L2StandardBridge contract. + constructor() StandardBridge() { + initialize({ _otherBridge: StandardBridge(payable(address(0))) }); } - /// @notice Returns the cross domain messenger. - function messenger() public pure override returns (ICrossDomainMessenger) { - return ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + /// @notice Initializer. + /// @param _otherBridge Contract for the corresponding bridge on the other chain. + function initialize(StandardBridge _otherBridge) public initializer { + __StandardBridge_init({ + _messenger: ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER), + _otherBridge: _otherBridge + }); } /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. @@ -146,7 +146,7 @@ contract L2StandardBridge is StandardBridge, ISemver { /// @notice Retrieves the access of the corresponding L1 bridge contract. /// @return Address of the corresponding L1 bridge contract. function l1TokenBridge() external view returns (address) { - return address(otherBridge()); + return address(otherBridge); } /// @custom:legacy diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol index 622d92187238..e17ef29dd964 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol @@ -40,9 +40,9 @@ contract L2StandardBridgeInterop is L2StandardBridge { event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); /// @notice Semantic version. - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); + return string.concat(super.version(), "+interop-beta.2"); } /// @notice Converts `amount` of `from` token to `to` token. diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index 7a6342dc07e3..6b1d7327dbc0 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -65,8 +65,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.11 - string public constant version = "1.0.0-beta.11"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again. diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index 86f73f1d6f95..c323d8b7577b 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -58,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.10 - string public constant override version = "1.0.0-beta.10"; + /// @custom:semver 1.0.0-beta.9 + string public constant override version = "1.0.0-beta.9"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol index 6f79097b16e9..e2b3dc437b0f 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol @@ -11,8 +11,8 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; /// @notice OptimismSuperchainERC20Beacon is the beacon proxy for the OptimismSuperchainERC20 implementation. contract OptimismSuperchainERC20Beacon is IBeacon, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.3 - string public constant version = "1.0.0-beta.3"; + /// @custom:semver 1.0.0-beta.2 + string public constant version = "1.0.0-beta.2"; /// @inheritdoc IBeacon function implementation() external pure override returns (address) { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol index 505bfdc37291..454e3b455d62 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol @@ -22,8 +22,8 @@ contract OptimismSuperchainERC20Factory is ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.5 - string public constant version = "1.0.0-beta.5"; + /// @custom:semver 1.0.0-beta.4 + string public constant version = "1.0.0-beta.4"; /// @notice Mapping of the deployed OptimismSuperchainERC20 to the remote token address. /// This is used to keep track of the token deployments. diff --git a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol index 965948e42e5f..69a78219e5bd 100644 --- a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { FeeVault } from "src/L2/FeeVault.sol"; + import { Types } from "src/libraries/Types.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000011 @@ -12,27 +12,25 @@ import { Encoding } from "src/libraries/Encoding.sol"; /// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during /// transaction processing and block production. contract SequencerFeeVault is FeeVault, ISemver { - /// @custom:semver 1.5.0-beta.4 - string public constant version = "1.5.0-beta.4"; + /// @custom:semver 1.5.0-beta.3 + string public constant version = "1.5.0-beta.3"; - /// @notice Returns the FeeVault config - /// @return recipient_ Wallet that will receive the fees. - /// @return amount_ Minimum balance for withdrawals. - /// @return withdrawalNetwork_ Network which the recipient will receive fees on. - function config() - public - view - override - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_) - { - bytes memory data = L1_BLOCK().getConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); - (recipient_, amount_, withdrawalNetwork_) = Encoding.decodeFeeVaultConfig(abi.decode(data, (bytes32))); - } + /// @notice Constructs the SequencerFeeVault contract. + /// @param _recipient Wallet that will receive the fees. + /// @param _minWithdrawalAmount Minimum balance for withdrawals. + /// @param _withdrawalNetwork Network which the recipient will receive fees on. + constructor( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) + { } /// @custom:legacy /// @notice Legacy getter for the recipient address. - /// @return recipient_ The recipient address. - function l1FeeWallet() public view returns (address recipient_) { - recipient_ = recipient(); + /// @return The recipient address. + function l1FeeWallet() public view returns (address) { + return RECIPIENT; } } diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index 14d62c02952b..29e179eba82c 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -24,8 +24,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// do not use a custom gas token. contract SuperchainWETH is WETH98, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.11 - string public constant version = "1.0.0-beta.11"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @inheritdoc WETH98 function deposit() public payable override { diff --git a/packages/contracts-bedrock/src/L2/WETH.sol b/packages/contracts-bedrock/src/L2/WETH.sol index 8580b99e9522..dacd62c36de9 100644 --- a/packages/contracts-bedrock/src/L2/WETH.sol +++ b/packages/contracts-bedrock/src/L2/WETH.sol @@ -14,8 +14,8 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// @title WETH contract that reads the name and symbol from the L1Block contract. /// Allows for nice rendering of token names for chains using custom gas token. contract WETH is WETH98, ISemver { - /// @custom:semver 1.1.0-beta.4 - string public constant version = "1.1.0-beta.4"; + /// @custom:semver 1.1.0-beta.3 + string public constant version = "1.1.0-beta.3"; /// @notice Returns the name of the wrapped native asset. Will be "Wrapped Ether" /// if the native asset is Ether. diff --git a/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol index a195a2428093..7ce731f2e631 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IBaseFeeVault.sol @@ -11,17 +11,19 @@ interface IBaseFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); - function config() - external - view - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); + function version() external view returns (string memory); - function __constructor__() external; + function __constructor__( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol index f970bf61c347..3b4cd0209f13 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IFeeVault.sol @@ -16,16 +16,12 @@ interface IFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); - function config() - external - view - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol index 2b0a016b3571..a43b3c7c3963 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1Block.sol @@ -1,11 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Types } from "src/libraries/Types.sol"; - interface IL1Block { error NotDepositor(); - error UnsafeCast(); event GasPayingTokenSet(address indexed token, uint8 indexed decimals, bytes32 name, bytes32 symbol); @@ -20,15 +17,11 @@ interface IL1Block { function gasPayingTokenSymbol() external view returns (string memory symbol_); function hash() external view returns (bytes32); function isCustomGasToken() external view returns (bool); - function isIsthmus() external view returns (bool); function l1FeeOverhead() external view returns (uint256); function l1FeeScalar() external view returns (uint256); function number() external view returns (uint64); function sequenceNumber() external view returns (uint64); - function setConfig(Types.ConfigType _type, bytes memory _value) external; - function getConfig(Types.ConfigType _type) external view returns (bytes memory data_); function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; - function setIsthmus() external; function setL1BlockValues( uint64 _number, uint64 _timestamp, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol index bf3be29c0714..dd72e3fa6f89 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1BlockInterop.sol @@ -1,7 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Types } from "src/libraries/Types.sol"; +enum ConfigType { + SET_GAS_PAYING_TOKEN, + ADD_DEPENDENCY, + REMOVE_DEPENDENCY +} interface IL1BlockInterop { error AlreadyDependency(); @@ -10,7 +14,6 @@ interface IL1BlockInterop { error NotCrossL2Inbox(); error NotDependency(); error NotDepositor(); - error UnsafeCast(); event DependencyAdded(uint256 indexed chainId); event DependencyRemoved(uint256 indexed chainId); @@ -27,19 +30,16 @@ interface IL1BlockInterop { function gasPayingToken() external view returns (address addr_, uint8 decimals_); function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); - function getConfig(Types.ConfigType _type) external view returns (bytes memory data_); function hash() external view returns (bytes32); function isCustomGasToken() external view returns (bool); function isDeposit() external view returns (bool isDeposit_); - function isIsthmus() external view returns (bool); function isInDependencySet(uint256 _chainId) external view returns (bool); function l1FeeOverhead() external view returns (uint256); function l1FeeScalar() external view returns (uint256); function number() external view returns (uint64); function sequenceNumber() external view returns (uint64); - function setConfig(Types.ConfigType _type, bytes memory _value) external; + function setConfig(ConfigType _type, bytes memory _value) external; function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; - function setIsthmus() external; function setL1BlockValues( uint64 _number, uint64 _timestamp, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol index 7f1362a034ab..eb695a7e9a58 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL1FeeVault.sol @@ -9,20 +9,21 @@ interface IL1FeeVault { receive() external payable; - function config() - external - view - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); function version() external view returns (string memory); - function __constructor__() external; + function __constructor__( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol index 60f241d55723..1cb49f674ec0 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2CrossDomainMessenger.sol @@ -5,9 +5,8 @@ import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMess interface IL2CrossDomainMessenger is ICrossDomainMessenger { function MESSAGE_VERSION() external view returns (uint16); + function initialize(ICrossDomainMessenger _l1CrossDomainMessenger) external; function l1CrossDomainMessenger() external view returns (ICrossDomainMessenger); - function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); - function otherMessenger() external view returns (ICrossDomainMessenger); function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol index 6d3c1c1a4d60..a760ce1d803c 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2ERC721Bridge.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import { IERC721Bridge } from "src/universal/interfaces/IERC721Bridge.sol"; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; interface IL2ERC721Bridge is IERC721Bridge { function finalizeBridgeERC721( @@ -14,7 +13,7 @@ interface IL2ERC721Bridge is IERC721Bridge { bytes memory _extraData ) external; - function messenger() external pure returns (ICrossDomainMessenger); + function initialize(address payable _l1ERC721Bridge) external; function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol deleted file mode 100644 index 298465021ac6..000000000000 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2OptimismMintableERC20Factory.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IL2OptimismMintableERC20Factory { - event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); - event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); - - function BRIDGE() external view returns (address); - function bridge() external view returns (address); - function createOptimismMintableERC20( - address _remoteToken, - string memory _name, - string memory _symbol - ) - external - returns (address); - function createOptimismMintableERC20WithDecimals( - address _remoteToken, - string memory _name, - string memory _symbol, - uint8 _decimals - ) - external - returns (address); - function createStandardL2Token( - address _remoteToken, - string memory _name, - string memory _symbol - ) - external - returns (address); - function deployments(address) external view returns (address); - function version() external view returns (string memory); - - function __constructor__() external; -} diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol index 3526ae5a49de..9f9ce1a85621 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridge.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; interface IL2StandardBridge is IStandardBridge { event DepositFinalized( @@ -24,13 +23,8 @@ interface IL2StandardBridge is IStandardBridge { receive() external payable; + function initialize(IStandardBridge _otherBridge) external; function l1TokenBridge() external view returns (address); - function MESSENGER() external view returns (ICrossDomainMessenger); - function messenger() external pure returns (ICrossDomainMessenger); - - function OTHER_BRIDGE() external view returns (IStandardBridge); - function otherBridge() external view returns (IStandardBridge); - function version() external pure returns (string memory); function withdraw( address _l2Token, diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol index aaed62e14f0a..6b60f5e4f9b2 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2StandardBridgeInterop.sol @@ -31,6 +31,8 @@ interface IL2StandardBridgeInterop is IStandardBridge { bytes extraData ); + function MESSENGER() external view returns (ICrossDomainMessenger); + function OTHER_BRIDGE() external view returns (IStandardBridge); function bridgeERC20( address _localToken, address _remoteToken, @@ -61,15 +63,11 @@ interface IL2StandardBridgeInterop is IStandardBridge { ) external; function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _extraData) external payable; - - function MESSENGER() external view returns (ICrossDomainMessenger); - function messenger() external pure returns (ICrossDomainMessenger); - - function OTHER_BRIDGE() external view returns (IStandardBridge); + function messenger() external view returns (ICrossDomainMessenger); function otherBridge() external view returns (IStandardBridge); - function paused() external view returns (bool); + function initialize(IStandardBridge _otherBridge) external; function l1TokenBridge() external view returns (address); function withdraw( address _l2Token, diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol b/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol index f6201268134a..e4ae46413eff 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISequencerFeeVault.sol @@ -11,18 +11,20 @@ interface ISequencerFeeVault { function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256); function RECIPIENT() external view returns (address); - function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function WITHDRAWAL_NETWORK() external view returns (Types.WithdrawalNetwork); function minWithdrawalAmount() external view returns (uint256 amount_); function recipient() external view returns (address recipient_); function totalProcessed() external view returns (uint256); function withdraw() external; - function withdrawalNetwork() external view returns (Types.WithdrawalNetwork withdrawalNetwork_); + function withdrawalNetwork() external view returns (Types.WithdrawalNetwork network_); function version() external view returns (string memory); - function l1FeeWallet() external view returns (address recipient_); - function config() - external - view - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork withdrawalNetwork_); - function __constructor__() external; + function l1FeeWallet() external view returns (address); + + function __constructor__( + address _recipient, + uint256 _minWithdrawalAmount, + Types.WithdrawalNetwork _withdrawalNetwork + ) + external; } diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 9d8ffe7b76fb..84d5f732f5f6 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -8,9 +8,6 @@ import { RLPWriter } from "src/libraries/rlp/RLPWriter.sol"; /// @title Encoding /// @notice Encoding handles Optimism's various different encoding schemes. library Encoding { - /// @notice Error to be used when an unsafe cast is attempted. - error UnsafeCast(); - /// @notice RLP encodes the L2 transaction that would be generated when a given deposit is sent /// to the L2 system. Useful for searching for a deposit in the L2 system. The /// transaction is prefixed with 0x7e to identify its EIP-2718 type. @@ -139,39 +136,6 @@ library Encoding { return (nonce, version); } - /// @notice Encodes a fee vault configuration. - /// @param _recipient Address of the recipient of the fee vault. - /// @param _amount Amount of the fee vault. - /// @param _network Network of the fee vault. - /// @return Encoded fee vault configuration. - function encodeFeeVaultConfig( - address _recipient, - uint256 _amount, - Types.WithdrawalNetwork _network - ) - internal - pure - returns (bytes32) - { - if (_amount > type(uint88).max) revert UnsafeCast(); - return bytes32(uint256(_network) << 248 | _amount << 160 | uint256(uint160(_recipient))); - } - - /// @notice Decodes a fee vault configuration. - /// @param _data Encoded fee vault configuration. - /// @return recipient_ Recipient of the fee vault. - /// @return amount_ Amount of the fee vault. - /// @return network_ Network of the fee vault. - function decodeFeeVaultConfig(bytes32 _data) - internal - pure - returns (address recipient_, uint256 amount_, Types.WithdrawalNetwork network_) - { - recipient_ = address(uint160(uint256(_data) & uint256(type(uint160).max))); - amount_ = (uint256(_data) & uint256(type(uint88).max) << 160) >> 160; - network_ = Types.WithdrawalNetwork(uint8(uint256(_data >> 248))); - } - /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesEcotone /// @param _baseFeeScalar L1 base fee Scalar /// @param _blobBaseFeeScalar L1 blob base fee Scalar diff --git a/packages/contracts-bedrock/src/libraries/Predeploys.sol b/packages/contracts-bedrock/src/libraries/Predeploys.sol index 7a6b30100b5a..30cad3b1f854 100644 --- a/packages/contracts-bedrock/src/libraries/Predeploys.sol +++ b/packages/contracts-bedrock/src/libraries/Predeploys.sol @@ -119,13 +119,13 @@ library Predeploys { if (_addr == GAS_PRICE_ORACLE) return "GasPriceOracle"; if (_addr == L2_STANDARD_BRIDGE) return "L2StandardBridge"; if (_addr == SEQUENCER_FEE_WALLET) return "SequencerFeeVault"; - if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "L2OptimismMintableERC20Factory"; + if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "OptimismMintableERC20Factory"; if (_addr == L1_BLOCK_NUMBER) return "L1BlockNumber"; if (_addr == L2_ERC721_BRIDGE) return "L2ERC721Bridge"; if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block"; if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser"; if (_addr == OPTIMISM_MINTABLE_ERC721_FACTORY) return "OptimismMintableERC721Factory"; - if (_addr == PROXY_ADMIN) return "L2ProxyAdmin"; + if (_addr == PROXY_ADMIN) return "ProxyAdmin"; if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry"; diff --git a/packages/contracts-bedrock/src/libraries/StaticConfig.sol b/packages/contracts-bedrock/src/libraries/StaticConfig.sol index 9c16205e281d..ffaa0b4e5535 100644 --- a/packages/contracts-bedrock/src/libraries/StaticConfig.sol +++ b/packages/contracts-bedrock/src/libraries/StaticConfig.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Types } from "src/libraries/Types.sol"; - /// @title StaticConfig /// @notice Library for encoding and decoding static configuration data. library StaticConfig { @@ -59,60 +57,4 @@ library StaticConfig { function decodeRemoveDependency(bytes memory _data) internal pure returns (uint256) { return abi.decode(_data, (uint256)); } - - /// @notice Encodes the static configuration data for setting a fee vault config. - /// @param _recipient Address of the recipient of the fee vault. - /// @param _min Minimum withdrawal amount allowed to be processed. - /// @param _network The network in which the fees should be withdrawn to. - /// @return Encoded static configuration data. - function encodeSetFeeVaultConfig( - address _recipient, - uint256 _min, - Types.WithdrawalNetwork _network - ) - internal - pure - returns (bytes memory) - { - return abi.encode(_recipient, _min, _network); - } - - /// @notice Decodes the static configuration data for setting a fee vault config. - /// @param _data Encoded static configuration data. - /// @return Decoded fee vault config data (recipient, min, network). - function decodeSetFeeVaultConfig(bytes memory _data) - internal - pure - returns (address, uint256, Types.WithdrawalNetwork) - { - return abi.decode(_data, (address, uint256, Types.WithdrawalNetwork)); - } - - /// @notice Encodes the static configuration data for setting an address. - /// @param _address Address to set. - /// @return Encoded static configuration data. - function encodeSetAddress(address _address) internal pure returns (bytes memory) { - return abi.encode(_address); - } - - /// @notice Decodes the static configuration data for setting an address. - /// @param _data Encoded static configuration data. - /// @return Decoded address. - function decodeSetAddress(bytes memory _data) internal pure returns (address) { - return abi.decode(_data, (address)); - } - - /// @notice Encodes the static configuration data for setting a remote chain ID. - /// @param _chainId Chain ID of the remote chain. - /// @return Encoded static configuration data. - function encodeSetRemoteChainId(uint256 _chainId) internal pure returns (bytes memory) { - return abi.encode(_chainId); - } - - /// @notice Decodes the static configuration data for setting a remote chain ID. - /// @param _data Encoded static configuration data. - /// @return Decoded chain ID of the remote chain. - function decodeSetRemoteChainId(bytes memory _data) internal pure returns (uint256) { - return abi.decode(_data, (uint256)); - } } diff --git a/packages/contracts-bedrock/src/libraries/Types.sol b/packages/contracts-bedrock/src/libraries/Types.sol index ea1513df3693..7e9a65654bc1 100644 --- a/packages/contracts-bedrock/src/libraries/Types.sol +++ b/packages/contracts-bedrock/src/libraries/Types.sol @@ -75,29 +75,4 @@ library Types { L1, L2 } - - /// @notice Enum representing different types of configurations that can be set on L1BlockIsthmus. - /// @custom:value GAS_PAYING_TOKEN Config type for the gas paying token. - /// @custom:value BASE_FEE_VAULT_CONFIG Config type for the base fee vault config. - /// @custom:value L1_FEE_VAULT_CONFIG Config type for the L1 fee vault config. - /// @custom:value SEQUENCER_FEE_VAULT_CONFIG Config type for the sequencer fee vault config. - /// @custom:value L1_CROSS_DOMAIN_MESSENGER_ADDRESS Config type for the L1 Cross Domain Messenger address. - /// @custom:value L1_ERC_721_BRIDGE_ADDRESS Config type for the L1 ERC721 Bridge address. - /// @custom:value L1_STANDARD_BRIDGE_ADDRESS Config type for the L1 Standard Bridge address. - /// @custom:value REMOTE_CHAIN_ID Config type for the remote chain ID. - /// @custom:value ADD_DEPENDENCY Config type for adding a chain to the interop dependency set. - /// @custom:value REMOVE_DEPENDENCY Config type for removing a chain from the interop dependency - /// set. - enum ConfigType { - GAS_PAYING_TOKEN, - BASE_FEE_VAULT_CONFIG, - L1_FEE_VAULT_CONFIG, - SEQUENCER_FEE_VAULT_CONFIG, - L1_CROSS_DOMAIN_MESSENGER_ADDRESS, - L1_ERC_721_BRIDGE_ADDRESS, - L1_STANDARD_BRIDGE_ADDRESS, - REMOTE_CHAIN_ID, - ADD_DEPENDENCY, - REMOVE_DEPENDENCY - } } diff --git a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol index cf1ba049d428..66c724d3eba9 100644 --- a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol @@ -1,27 +1,30 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Encoding } from "src/libraries/Encoding.sol"; import { Constants } from "src/libraries/Constants.sol"; /// @custom:legacy -/// @title CrossDomainMessengerLegacySpacer +/// @title CrossDomainMessengerLegacySpacer0 /// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the -/// libAddressManager variable, PausableUpgradable and OwnableUpgradeable -/// variables used to exist. -abstract contract CrossDomainMessengerLegacySpacer { +/// libAddressManager variable used to exist. Must be the first contract in the inheritance +/// tree of the CrossDomainMessenger. +contract CrossDomainMessengerLegacySpacer0 { /// @custom:legacy /// @custom:spacer libAddressManager /// @notice Spacer for backwards compatibility. address private spacer_0_0_20; +} - /// @custom:legacy - /// @custom:spacer initializer - /// @notice Spacer for backwards compatibility. - bytes12 private spacer_0_20_12; - +/// @custom:legacy +/// @title CrossDomainMessengerLegacySpacer1 +/// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the +/// PausableUpgradable and OwnableUpgradeable variables used to exist. Must be +/// the third contract in the inheritance tree of the CrossDomainMessenger. +contract CrossDomainMessengerLegacySpacer1 { /// @custom:legacy /// @custom:spacer ContextUpgradable's __gap /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin @@ -31,7 +34,7 @@ abstract contract CrossDomainMessengerLegacySpacer { /// @custom:legacy /// @custom:spacer OwnableUpgradeable's _owner /// @notice Spacer for backwards compatibility. - /// Comes from OpenZeppelin OwnableUpgradeable. + /// Come from OpenZeppelin OwnableUpgradeable. address private spacer_51_0_20; /// @custom:legacy @@ -81,7 +84,11 @@ abstract contract CrossDomainMessengerLegacySpacer { /// chain it's deployed on. Currently only designed for message passing between two paired /// chains and does not support one-to-many interactions. /// Any changes to this contract MUST result in a semver bump for contracts that inherit it. -abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { +abstract contract CrossDomainMessenger is + CrossDomainMessengerLegacySpacer0, + Initializable, + CrossDomainMessengerLegacySpacer1 +{ /// @notice Current message version identifier. uint16 public constant MESSAGE_VERSION = 1; @@ -112,12 +119,11 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { /// can therefore not be relayed again. mapping(bytes32 => bool) public successfulMessages; - /// @custom:legacy - /// @custom:spacer xDomainMsgSender - /// @notice Spacer for backwards compatibility. The storage slot was migrated when the - /// initializer pattern was moved away from in the base contract to remove the - /// need to set `Constants.DEFAULT_L2_SENDER` into storage during a call to `initialize`. - bytes20 internal spacer_204_0_20; + /// @notice Address of the sender of the currently executing message on the other chain. If the + /// value of this variable is the default value (0x00000000...dead) then no message is + /// currently being executed. Use the xDomainMessageSender getter which will throw an + /// error if this is the case. + address internal xDomainMsgSender; /// @notice Nonce for the next message to be sent, without the message version applied. Use the /// messageNonce getter which will insert the message version into the nonce to give you @@ -129,23 +135,14 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { /// successfully executed on the first attempt. mapping(bytes32 => bool) public failedMessages; - /// @custom:legacy - /// @custom:spacer CrossDomainMessenger - /// @notice Spacer for backwards compatibility. - address private spacer_207_0_20; - - /// @notice Address of the sender of the currently executing message on the other chain. If the - /// value of this variable is address(0) then no message is currently being executed. - /// Use the xDomainMessageSender getter which will throw an error if this is the case. - address private xDomainMsgSender; - - /// @notice Spacer to ensure that there is no collision with the xDomainMsgSender slot. - bytes12 private spacer_208_20_12; + /// @notice CrossDomainMessenger contract on the other chain. + /// @custom:network-specific + CrossDomainMessenger public otherMessenger; /// @notice Reserve extra slots in the storage layout for future upgrades. - /// A gap size of 40 was chosen here, so that the first slot used in a child contract + /// A gap size of 43 was chosen here, so that the first slot used in a child contract /// would be 1 plus a multiple of 50. - uint256[41] private __gap; + uint256[43] private __gap; /// @notice Emitted whenever a message is sent to the other chain. /// @param target Address of the recipient of the message. @@ -186,7 +183,7 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { // guarantee the property that the call to the target contract will always have at least // the minimum gas limit specified by the user. _sendMessage({ - _to: address(otherMessenger()), + _to: address(otherMessenger), _gasLimit: baseGas(_message, _minGasLimit), _value: msg.value, _data: abi.encodeWithSelector( @@ -269,7 +266,7 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { // is being re-entered. This marks the message as failed to allow it to be replayed. if ( !SafeCall.hasMinGas(_minGasLimit, RELAY_RESERVED_GAS + RELAY_GAS_CHECK_BUFFER) - || getCrossDomainMessageSender() != Constants.DEFAULT_L2_SENDER + || xDomainMsgSender != Constants.DEFAULT_L2_SENDER ) { failedMessages[versionedHash] = true; emit FailedRelayedMessage(versionedHash); @@ -286,9 +283,9 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { return; } - setCrossDomainMessageSender(_sender); + xDomainMsgSender = _sender; bool success = SafeCall.call(_target, gasleft() - RELAY_RESERVED_GAS, _value, _message); - setCrossDomainMessageSender(address(0)); + xDomainMsgSender = Constants.DEFAULT_L2_SENDER; if (success) { // This check is identical to one above, but it ensures that the same message cannot be relayed @@ -311,40 +308,24 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { } } - /// @notice Retrieves the address of the contract or wallet that initiated the currently - /// executing message on the other chain. - /// @return Address of the sender of the currently executing message on the other chain. - function getCrossDomainMessageSender() internal view returns (address) { - if (xDomainMsgSender == address(0)) return Constants.DEFAULT_L2_SENDER; - return xDomainMsgSender; - } - - /// @notice Setter function for the cross-domain message sender. - /// @param _address Address of the sender of the currently executing message on the other chain. - function setCrossDomainMessageSender(address _address) internal { - xDomainMsgSender = _address; - } - /// @notice Retrieves the address of the contract or wallet that initiated the currently /// executing message on the other chain. Will throw an error if there is no message /// currently being executed. Allows the recipient of a call to see who triggered it. /// @return Address of the sender of the currently executing message on the other chain. function xDomainMessageSender() external view returns (address) { - address sender = getCrossDomainMessageSender(); - require(sender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set"); - return sender; - } + require( + xDomainMsgSender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set" + ); - /// @notice Returns the contract of the other messenger on this chain. - /// @return Contract of the other messenger on this chain. - function otherMessenger() public view virtual returns (CrossDomainMessenger); + return xDomainMsgSender; + } /// @notice Retrieves the address of the paired CrossDomainMessenger contract on the other chain /// Public getter is legacy and will be removed in the future. Use `otherMessenger()` instead. /// @return CrossDomainMessenger contract on the other chain. /// @custom:legacy function OTHER_MESSENGER() public view returns (CrossDomainMessenger) { - return otherMessenger(); + return otherMessenger; } /// @notice Retrieves the next message nonce. Message version will be added to the upper two @@ -390,6 +371,19 @@ abstract contract CrossDomainMessenger is CrossDomainMessengerLegacySpacer { return token != Constants.ETHER; } + /// @notice Initializer. + /// @param _otherMessenger CrossDomainMessenger contract on the other chain. + function __CrossDomainMessenger_init(CrossDomainMessenger _otherMessenger) internal onlyInitializing { + // We only want to set the xDomainMsgSender to the default value if it hasn't been initialized yet, + // meaning that this is a fresh contract deployment. + // This prevents resetting the xDomainMsgSender to the default value during an upgrade, which would enable + // a reentrant withdrawal to sandwhich the upgrade replay a withdrawal twice. + if (xDomainMsgSender == address(0)) { + xDomainMsgSender = Constants.DEFAULT_L2_SENDER; + } + otherMessenger = _otherMessenger; + } + /// @notice Sends a low-level message to the other messenger. Needs to be implemented by child /// contracts because the logic for this depends on the network where the messenger is /// being deployed. diff --git a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol index 60edc5a55b57..52217fab713c 100644 --- a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol @@ -1,25 +1,24 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.25; +pragma solidity 0.8.15; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; /// @title ERC721Bridge /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges. -abstract contract ERC721Bridge { +abstract contract ERC721Bridge is Initializable { /// @custom:spacer ERC721Bridge's initializer slot spacing - /// @notice Spacer for legacy initializable slot - bytes32 private spacer_0_0_32; + /// @notice Spacer to avoid packing into the initializer slot + bytes30 private spacer_0_2_30; - /// @custom:legacy - /// @custom:spacer messenger - /// @notice Spacer for backwards compatibility. - address private spacer_1_0_20; + /// @notice Messenger contract on this domain. + /// @custom:network-specific + ICrossDomainMessenger public messenger; - /// @custom:legacy - /// @custom:spacer otherBridge - /// @notice Spacer for backwards compatibility. - address private spacer_2_0_20; + /// @notice Contract of the bridge on the other network. + /// @custom:network-specific + ERC721Bridge public otherBridge; /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. uint256[46] private __gap; @@ -58,37 +57,41 @@ abstract contract ERC721Bridge { /// @notice Ensures that the caller is a cross-chain message from the other bridge. modifier onlyOtherBridge() { - ICrossDomainMessenger crossDomainMessenger = messenger(); require( - msg.sender == address(crossDomainMessenger) - && crossDomainMessenger.xDomainMessageSender() == address(otherBridge()), + msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge), "ERC721Bridge: function can only be called from the other bridge" ); _; } - /// @notice Getter function for the messenger contract. - /// @return Messenger contract on this domain. - function messenger() public view virtual returns (ICrossDomainMessenger); + /// @notice Initializer. + /// @param _messenger Contract of the CrossDomainMessenger on this network. + /// @param _otherBridge Contract of the ERC721 bridge on the other network. + function __ERC721Bridge_init( + ICrossDomainMessenger _messenger, + ERC721Bridge _otherBridge + ) + internal + onlyInitializing + { + messenger = _messenger; + otherBridge = _otherBridge; + } /// @notice Legacy getter for messenger contract. /// Public getter is legacy and will be removed in the future. Use `messenger` instead. /// @return Messenger contract on this domain. /// @custom:legacy function MESSENGER() external view returns (ICrossDomainMessenger) { - return messenger(); + return messenger; } - /// @notice Getter function for the other bridge. - /// @return Contract of the bridge on the other network. - function otherBridge() public view virtual returns (ERC721Bridge); - /// @notice Legacy getter for other bridge address. /// Public getter is legacy and will be removed in the future. Use `otherBridge` instead. /// @return Contract of the bridge on the other network. /// @custom:legacy function OTHER_BRIDGE() external view returns (ERC721Bridge) { - return otherBridge(); + return otherBridge; } /// @notice This function should return true if the contract is paused. diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol index 788ef2b2f546..d7a9cd3372cd 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol @@ -3,21 +3,24 @@ pragma solidity 0.8.15; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IOptimismERC20Factory } from "src/L2/interfaces/IOptimismERC20Factory.sol"; +/// @custom:proxied true +/// @custom:predeployed 0x4200000000000000000000000000000000000012 /// @title OptimismMintableERC20Factory -/// @notice OptimismMintableERC20Factory is an abstract factory contract that generates OptimismMintableERC20 -/// contracts on the network it's deployed to. It should be inherited by a child contract that -/// implements the `bridge` getter function. -/// Any changes to this contract MUST result in a semver bump for contracts that inherit it. -abstract contract OptimismMintableERC20Factory is ISemver, IOptimismERC20Factory { +/// @notice OptimismMintableERC20Factory is a factory contract that generates OptimismMintableERC20 +/// contracts on the network it's deployed to. Simplifies the deployment process for users +/// who may be less familiar with deploying smart contracts. Designed to be backwards +/// compatible with the older StandardL2ERC20Factory contract. +contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20Factory { /// @custom:spacer OptimismMintableERC20Factory's initializer slot spacing /// @notice Spacer to avoid packing into the initializer slot - bytes32 private spacer_0_0_32; + bytes30 private spacer_0_2_30; - /// @custom:spacer bridge - /// @notice Spacer to avoid packing into the initializer slot - bytes32 private spacer_1_0_32; + /// @notice Address of the StandardBridge on this chain. + /// @custom:network-specific + address public bridge; /// @notice Mapping of local token address to remote token address. /// This is used to keep track of the token deployments. @@ -41,14 +44,30 @@ abstract contract OptimismMintableERC20Factory is ISemver, IOptimismERC20Factory /// @param deployer Address of the account that deployed the token. event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); - function bridge() public view virtual returns (address); + /// @notice The semver MUST be bumped any time that there is a change in + /// the OptimismMintableERC20 token contract since this contract + /// is responsible for deploying OptimismMintableERC20 contracts. + /// @notice Semantic version. + /// @custom:semver 1.10.1-beta.4 + string public constant version = "1.10.1-beta.4"; + + /// @notice Constructs the OptimismMintableERC20Factory contract. + constructor() { + initialize({ _bridge: address(0) }); + } + + /// @notice Initializes the contract. + /// @param _bridge Address of the StandardBridge on this chain. + function initialize(address _bridge) public initializer { + bridge = _bridge; + } /// @notice Getter function for the address of the StandardBridge on this chain. /// Public getter is legacy and will be removed in the future. Use `bridge` instead. /// @return Address of the StandardBridge on this chain. /// @custom:legacy function BRIDGE() external view returns (address) { - return bridge(); + return bridge; } /// @custom:legacy @@ -105,7 +124,7 @@ abstract contract OptimismMintableERC20Factory is ISemver, IOptimismERC20Factory bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol, _decimals)); address localToken = - address(new OptimismMintableERC20{ salt: salt }(bridge(), _remoteToken, _name, _symbol, _decimals)); + address(new OptimismMintableERC20{ salt: salt }(bridge, _remoteToken, _name, _symbol, _decimals)); deployments[localToken] = _remoteToken; diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol index 692e8546b923..9dd05e10d1fe 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol @@ -5,7 +5,6 @@ import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extension import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; -import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title OptimismMintableERC721 @@ -30,20 +29,20 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { address public immutable REMOTE_TOKEN; /// @notice Address of the ERC721 bridge on this network. - IL2ERC721Bridge public immutable BRIDGE; + address public immutable BRIDGE; /// @notice Base token URI for this token. string public baseTokenURI; /// @notice Modifier that prevents callers other than the bridge from calling the function. modifier onlyBridge() { - require(msg.sender == address(BRIDGE), "OptimismMintableERC721: only bridge can call this function"); + require(msg.sender == BRIDGE, "OptimismMintableERC721: only bridge can call this function"); _; } /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.4 - string public constant version = "1.3.1-beta.4"; + /// @custom:semver 1.3.1-beta.3 + string public constant version = "1.3.1-beta.3"; /// @param _bridge Address of the bridge on this network. /// @param _remoteChainId Chain ID where the remote token is deployed. @@ -51,7 +50,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { /// @param _name ERC721 name. /// @param _symbol ERC721 symbol. constructor( - IL2ERC721Bridge _bridge, + address _bridge, uint256 _remoteChainId, address _remoteToken, string memory _name, @@ -59,7 +58,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { ) ERC721(_name, _symbol) { - require(address(_bridge) != address(0), "OptimismMintableERC721: bridge cannot be address(0)"); + require(_bridge != address(0), "OptimismMintableERC721: bridge cannot be address(0)"); require(_remoteChainId != 0, "OptimismMintableERC721: remote chain id cannot be zero"); require(_remoteToken != address(0), "OptimismMintableERC721: remote token cannot be address(0)"); @@ -91,8 +90,8 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { } /// @notice Address of the ERC721 bridge on this network. - function bridge() external view returns (IL2ERC721Bridge) { - return IL2ERC721Bridge(BRIDGE); + function bridge() external view returns (address) { + return BRIDGE; } /// @notice Mints some token ID for a user, checking first that contract recipients diff --git a/packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol similarity index 51% rename from packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol rename to packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol index a6818a83c9e6..7350e0fae0de 100644 --- a/packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol @@ -2,18 +2,19 @@ pragma solidity 0.8.15; import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; -import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; /// @title OptimismMintableERC721Factory /// @notice Factory contract for creating OptimismMintableERC721 contracts. -/// This contract could in theory live on both L1 and L2 but it is not widely -/// used and is therefore set up to work on L2. This could be abstracted in the -/// future to be deployable on L1 as well. contract OptimismMintableERC721Factory is ISemver { + /// @custom:legacy true + /// @notice Address of the ERC721 bridge on this network. + address public immutable BRIDGE; + + /// @custom:legacy true + /// @notice Chain ID for the remote network. + uint256 public immutable REMOTE_CHAIN_ID; + /// @notice Tracks addresses created by this factory. mapping(address => bool) public isOptimismMintableERC721; @@ -24,34 +25,27 @@ contract OptimismMintableERC721Factory is ISemver { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); /// @notice Semantic version. - /// The semver MUST be bumped any time that there is a change in - /// the OptimismMintableERC721 token contract since this contract - /// is responsible for deploying OptimismMintableERC721 contracts. /// @custom:semver 1.4.1-beta.4 string public constant version = "1.4.1-beta.4"; - /// @notice Returns the remote chain id - function REMOTE_CHAIN_ID() external view returns (uint256) { - return remoteChainId(); - } - - /// @notice Getter function for the remote chain id. - function remoteChainId() public view returns (uint256) { - bytes memory data = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).getConfig(Types.ConfigType.REMOTE_CHAIN_ID); - return abi.decode(data, (uint256)); + /// @notice The semver MUST be bumped any time that there is a change in + /// the OptimismMintableERC721 token contract since this contract + /// is responsible for deploying OptimismMintableERC721 contracts. + /// @param _bridge Address of the ERC721 bridge on this network. + /// @param _remoteChainId Chain ID for the remote network. + constructor(address _bridge, uint256 _remoteChainId) { + BRIDGE = _bridge; + REMOTE_CHAIN_ID = _remoteChainId; } - /// @notice Getter function for the bridge contract. - /// Public getter is legacy and will be removed in the future. Use `bridge()` instead. - /// @return Bridge contract on this domain. - /// @custom:legacy - function BRIDGE() external pure returns (IL2ERC721Bridge) { - return bridge(); + /// @notice Address of the ERC721 bridge on this network. + function bridge() external view returns (address) { + return BRIDGE; } - /// @notice Returns the ERC721 bridge contract. - function bridge() public pure returns (IL2ERC721Bridge) { - return IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE); + /// @notice Chain ID for the remote network. + function remoteChainID() external view returns (uint256) { + return REMOTE_CHAIN_ID; } /// @notice Creates an instance of the standard ERC721. @@ -70,7 +64,7 @@ contract OptimismMintableERC721Factory is ISemver { bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol)); address localToken = - address(new OptimismMintableERC721{ salt: salt }(bridge(), remoteChainId(), _remoteToken, _name, _symbol)); + address(new OptimismMintableERC721{ salt: salt }(BRIDGE, REMOTE_CHAIN_ID, _remoteToken, _name, _symbol)); isOptimismMintableERC721[localToken] = true; emit OptimismMintableERC721Created(localToken, _remoteToken, msg.sender); diff --git a/packages/contracts-bedrock/src/universal/StandardBridge.sol b/packages/contracts-bedrock/src/universal/StandardBridge.sol index 42b891c169fb..57af2247a65a 100644 --- a/packages/contracts-bedrock/src/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/StandardBridge.sol @@ -9,8 +9,8 @@ import { SafeCall } from "src/libraries/SafeCall.sol"; import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { Constants } from "src/libraries/Constants.sol"; /// @custom:upgradeable @@ -18,16 +18,16 @@ import { Constants } from "src/libraries/Constants.sol"; /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles /// the core bridging logic, including escrowing tokens that are native to the local chain /// and minting/burning tokens that are native to the remote chain. -abstract contract StandardBridge { +abstract contract StandardBridge is Initializable { using SafeERC20 for IERC20; /// @notice The L2 gas limit set when eth is depoisited using the receive() function. uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000; /// @custom:legacy - /// @custom:spacer messenger + initializable + /// @custom:spacer messenger /// @notice Spacer for backwards compatibility. - bytes32 private spacer_0_0_32; + bytes30 private spacer_0_2_30; /// @custom:legacy /// @custom:spacer l2TokenBridge @@ -37,20 +37,18 @@ abstract contract StandardBridge { /// @notice Mapping that stores deposits for a given pair of local and remote tokens. mapping(address => mapping(address => uint256)) public deposits; - /// @custom:legacy - /// @custom:spacer messenger - /// @notice Spacer for backwards compatibility. - address private spacer_3_0_20; + /// @notice Messenger contract on this domain. + /// @custom:network-specific + ICrossDomainMessenger public messenger; - /// @custom:legacy - /// @custom:spacer otherBridge - /// @notice Spacer for backwards compatibility. - address private spacer_4_0_20; + /// @notice Corresponding bridge on the other domain. + /// @custom:network-specific + StandardBridge public otherBridge; /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. - /// The gap size was previously 45, but is now 44, allowing for the initializer slot to be - /// included in the L1StandardBridge contract without breaking its storage layout. - uint256[44] private __gap; + /// A gap size of 45 was chosen here, so that the first slot used in a child contract + /// would be a multiple of 50. + uint256[45] private __gap; /// @notice Emitted when an ETH bridge is initiated to the other chain. /// @param from Address of the sender. @@ -108,15 +106,27 @@ abstract contract StandardBridge { /// @notice Ensures that the caller is a cross-chain message from the other bridge. modifier onlyOtherBridge() { - ICrossDomainMessenger crossDomainMessenger = messenger(); require( - msg.sender == address(crossDomainMessenger) - && crossDomainMessenger.xDomainMessageSender() == address(otherBridge()), + msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge), "StandardBridge: function can only be called from the other bridge" ); _; } + /// @notice Initializer. + /// @param _messenger Contract for CrossDomainMessenger on this network. + /// @param _otherBridge Contract for the other StandardBridge contract. + function __StandardBridge_init( + ICrossDomainMessenger _messenger, + StandardBridge _otherBridge + ) + internal + onlyInitializing + { + messenger = _messenger; + otherBridge = _otherBridge; + } + /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. /// Must be implemented by contracts that inherit. receive() external payable virtual; @@ -130,28 +140,20 @@ abstract contract StandardBridge { return token != Constants.ETHER; } - /// @notice Returns the contract of the CrossDomainMessenger on this chain. - /// @return Contract of the CrossDomainMessenger on this chain. - function messenger() public view virtual returns (ICrossDomainMessenger); - /// @notice Getter for messenger contract. /// Public getter is legacy and will be removed in the future. Use `messenger` instead. /// @return Contract of the messenger on this domain. /// @custom:legacy function MESSENGER() external view returns (ICrossDomainMessenger) { - return messenger(); + return messenger; } - /// @notice Returns the contract of the bridge on the other chain. - /// @return Contract of the bridge on the other chain. - function otherBridge() public view virtual returns (IStandardBridge); - /// @notice Getter for the other bridge contract. /// Public getter is legacy and will be removed in the future. Use `otherBridge` instead. /// @return Contract of the bridge on the other network. /// @custom:legacy - function OTHER_BRIDGE() external view returns (IStandardBridge) { - return otherBridge(); + function OTHER_BRIDGE() external view returns (StandardBridge) { + return otherBridge; } /// @notice This function should return true if the contract is paused. @@ -254,7 +256,7 @@ abstract contract StandardBridge { require(isCustomGasToken() == false, "StandardBridge: cannot bridge ETH with custom gas token"); require(msg.value == _amount, "StandardBridge: amount sent does not match amount required"); require(_to != address(this), "StandardBridge: cannot send to self"); - require(_to != address(messenger()), "StandardBridge: cannot send to messenger"); + require(_to != address(messenger), "StandardBridge: cannot send to messenger"); // Emit the correct events. By default this will be _amount, but child // contracts may override this function in order to emit legacy events as well. @@ -327,8 +329,8 @@ abstract contract StandardBridge { // contracts may override this function in order to emit legacy events as well. _emitETHBridgeInitiated(_from, _to, _amount, _extraData); - messenger().sendMessage{ value: _amount }({ - _target: address(otherBridge()), + messenger.sendMessage{ value: _amount }({ + _target: address(otherBridge), _message: abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData), _minGasLimit: _minGasLimit }); @@ -372,8 +374,8 @@ abstract contract StandardBridge { // contracts may override this function in order to emit legacy events as well. _emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); - messenger().sendMessage({ - _target: address(otherBridge()), + messenger.sendMessage({ + _target: address(otherBridge), _message: abi.encodeWithSelector( this.finalizeBridgeERC20.selector, // Because this call will be executed on the remote chain, we reverse the order of diff --git a/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol b/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol index 479e8a6ba273..256b09fa56ef 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/ICrossDomainMessenger.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; interface ICrossDomainMessenger { event FailedRelayedMessage(bytes32 indexed msgHash); + event Initialized(uint8 version); event RelayedMessage(bytes32 indexed msgHash); event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); event SentMessageExtension1(address indexed sender, uint256 value); @@ -12,7 +13,6 @@ interface ICrossDomainMessenger { function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() external view returns (uint64); function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() external view returns (uint64); function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); - function otherMessenger() external view returns (ICrossDomainMessenger); function RELAY_CALL_OVERHEAD() external view returns (uint64); function RELAY_CONSTANT_OVERHEAD() external view returns (uint64); function RELAY_GAS_CHECK_BUFFER() external view returns (uint64); @@ -20,6 +20,7 @@ interface ICrossDomainMessenger { function baseGas(bytes memory _message, uint32 _minGasLimit) external pure returns (uint64); function failedMessages(bytes32) external view returns (bool); function messageNonce() external view returns (uint256); + function otherMessenger() external view returns (ICrossDomainMessenger); function paused() external view returns (bool); function relayMessage( uint256 _nonce, diff --git a/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol b/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol index fb1f0021aa05..3c97958c1033 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IERC721Bridge.sol @@ -20,6 +20,7 @@ interface IERC721Bridge { uint256 tokenId, bytes extraData ); + event Initialized(uint8 version); function MESSENGER() external view returns (ICrossDomainMessenger); function OTHER_BRIDGE() external view returns (IERC721Bridge); diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol index 36978feece9e..91f6eba6c175 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC20Factory.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; interface IOptimismMintableERC20Factory { + event Initialized(uint8 version); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); @@ -30,6 +31,7 @@ interface IOptimismMintableERC20Factory { external returns (address); function deployments(address) external view returns (address); + function initialize(address _bridge) external; function version() external view returns (string memory); function __constructor__() external; diff --git a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol index f678e1338dc5..7d745ad8436e 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721.sol @@ -1,14 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; - /// @title IOptimismMintableERC721 /// @notice Interface for contracts that are compatible with the OptimismMintableERC721 standard. /// Tokens that follow this standard can be easily transferred across the ERC721 bridge. interface IOptimismMintableERC721 { function __constructor__( - IL2ERC721Bridge _bridge, + address _bridge, uint256 _remoteChainId, address _remoteToken, string memory _name, @@ -66,13 +64,11 @@ interface IOptimismMintableERC721 { function REMOTE_TOKEN() external view returns (address); - /// @notice Address of the ERC721 bridge on this network. - function BRIDGE() external view returns (IL2ERC721Bridge); + function BRIDGE() external view returns (address); function remoteChainId() external view returns (uint256); function remoteToken() external view returns (address); - /// @notice Address of the ERC721 bridge on this network. - function bridge() external view returns (IL2ERC721Bridge); + function bridge() external view returns (address); } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol similarity index 66% rename from packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol rename to packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol index 013c0b73c1a6..be3e8ad05425 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IOptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IOptimismMintableERC721Factory.sol @@ -1,13 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; - interface IOptimismMintableERC721Factory { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); - function BRIDGE() external pure returns (IL2ERC721Bridge); - function bridge() external pure returns (IL2ERC721Bridge); + function BRIDGE() external view returns (address); + function REMOTE_CHAIN_ID() external view returns (uint256); + function bridge() external view returns (address); function createOptimismMintableERC721( address _remoteToken, string memory _name, @@ -16,9 +15,8 @@ interface IOptimismMintableERC721Factory { external returns (address); function isOptimismMintableERC721(address) external view returns (bool); - function REMOTE_CHAIN_ID() external view returns (uint256); - function remoteChainId() external view returns (uint256); + function remoteChainID() external view returns (uint256); function version() external view returns (string memory); - function __constructor__() external; + function __constructor__(address _bridge, uint256 _remoteChainId) external; } diff --git a/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol b/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol index 3fdc09df503b..406a172c0737 100644 --- a/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/interfaces/IStandardBridge.sol @@ -22,10 +22,12 @@ interface IStandardBridge { ); event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); + event Initialized(uint8 version); receive() external payable; function MESSENGER() external view returns (ICrossDomainMessenger); + function OTHER_BRIDGE() external view returns (IStandardBridge); function bridgeERC20( address _localToken, address _remoteToken, @@ -58,7 +60,6 @@ interface IStandardBridge { function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _extraData) external payable; function messenger() external view returns (ICrossDomainMessenger); function otherBridge() external view returns (IStandardBridge); - function OTHER_BRIDGE() external view returns (IStandardBridge); function paused() external view returns (bool); function __constructor__() external; diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 232142e9070c..5b2260fce992 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -70,8 +70,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { batcher: _doi.batcher(), unsafeBlockSigner: _doi.unsafeBlockSigner(), proposer: _doi.proposer(), - challenger: _doi.challenger(), - systemConfigFeeAdmin: msg.sender + challenger: _doi.challenger() }), basefeeScalar: _doi.basefeeScalar(), blobBasefeeScalar: _doi.blobBaseFeeScalar(), diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol index 41c85e463f99..028fc7587043 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol @@ -19,7 +19,6 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; -import { StaticConfig } from "src/libraries/StaticConfig.sol"; import "src/libraries/PortalErrors.sol"; // Interfaces @@ -68,7 +67,6 @@ contract OptimismPortal_Test is CommonTest { assertEq(optimismPortal.paused(), false); (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal.params(); assertEq(prevBaseFee, 1 gwei); - // someplace upstream there is a deposit tx assertEq(prevBoughtGas, 0); assertEq(prevBlockNum, uint64(block.number)); } @@ -459,7 +457,6 @@ contract OptimismPortal_Test is CommonTest { ) external { - bytes memory data = StaticConfig.encodeSetGasPayingToken(_token, _decimals, _name, _symbol); vm.expectEmit(address(optimismPortal)); emit TransactionDeposited( 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, @@ -470,12 +467,12 @@ contract OptimismPortal_Test is CommonTest { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) ) ); vm.prank(address(systemConfig)); - optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); + optimismPortal.setGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); } /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` @@ -494,13 +491,10 @@ contract OptimismPortal_Test is CommonTest { bytes32 name = GasPayingToken.sanitize(_name); bytes32 symbol = GasPayingToken.sanitize(_symbol); - bytes memory data = - StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); - vm.recordLogs(); vm.prank(address(systemConfig)); - optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); + optimismPortal.setGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); optimismPortal.depositTransaction({ @@ -508,7 +502,7 @@ contract OptimismPortal_Test is CommonTest { _value: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + _data: abi.encodeCall(IL1Block.setGasPayingToken, (_token, 18, name, symbol)) }); VmSafe.Log[] memory logs = vm.getRecordedLogs(); @@ -531,7 +525,7 @@ contract OptimismPortal_Test is CommonTest { vm.assume(_caller != address(systemConfig)); vm.prank(_caller); vm.expectRevert(Unauthorized.selector); - optimismPortal.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, hex""); + optimismPortal.setGasPayingToken({ _token: address(0), _decimals: 0, _name: "", _symbol: "" }); } /// @dev Tests that `depositERC20Transaction` reverts when the gas paying token is ether. diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index a400c3e5de32..8cc56937bd33 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -19,7 +19,6 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; -import { StaticConfig } from "src/libraries/StaticConfig.sol"; import "src/dispute/lib/Types.sol"; import "src/libraries/PortalErrors.sol"; @@ -309,13 +308,6 @@ contract OptimismPortal2_Test is CommonTest { ) external { - bytes memory data = StaticConfig.encodeSetGasPayingToken({ - _token: _token, - _decimals: _decimals, - _name: _name, - _symbol: _symbol - }); - vm.expectEmit(address(optimismPortal2)); emit TransactionDeposited( 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, @@ -326,12 +318,12 @@ contract OptimismPortal2_Test is CommonTest { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + abi.encodeCall(IL1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) ) ); vm.prank(address(systemConfig)); - optimismPortal2.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); + optimismPortal2.setGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }); } /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` @@ -350,12 +342,10 @@ contract OptimismPortal2_Test is CommonTest { bytes32 name = GasPayingToken.sanitize(_name); bytes32 symbol = GasPayingToken.sanitize(_symbol); - bytes memory data = StaticConfig.encodeSetGasPayingToken(_token, 18, name, symbol); - vm.recordLogs(); vm.prank(address(systemConfig)); - optimismPortal2.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, data); + optimismPortal2.setGasPayingToken({ _token: _token, _decimals: 18, _name: name, _symbol: symbol }); vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); optimismPortal2.depositTransaction({ @@ -363,70 +353,7 @@ contract OptimismPortal2_Test is CommonTest { _value: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) - }); - - VmSafe.Log[] memory logs = vm.getRecordedLogs(); - assertEq(logs.length, 2); - - VmSafe.Log memory systemPath = logs[0]; - VmSafe.Log memory userPath = logs[1]; - - assertEq(systemPath.topics.length, 4); - assertEq(systemPath.topics.length, userPath.topics.length); - assertEq(systemPath.topics[0], userPath.topics[0]); - assertEq(systemPath.topics[1], userPath.topics[1]); - assertEq(systemPath.topics[2], userPath.topics[2]); - assertEq(systemPath.topics[3], userPath.topics[3]); - assertEq(systemPath.data, userPath.data); - } - - /// @dev Tests that the upgrade function succeeds. - function testFuzz_upgrade_succeeds(uint32 _gasLimit, bytes memory _calldata) external { - address upgrader = superchainConfig.upgrader(); - - vm.expectEmit(address(optimismPortal2)); - emit TransactionDeposited( - 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, - Predeploys.PROXY_ADMIN, - 0, - abi.encodePacked( - uint256(0), // mint - uint256(0), // value - uint64(_gasLimit), // gasLimit - false, // isCreation, - _calldata - ) - ); - - vm.prank(upgrader); - optimismPortal2.upgrade(_gasLimit, _calldata); - } - - /// @notice Ensures that the deposit event is correct for the `setGasPayingToken` - /// code path that manually emits a deposit transaction outside of the - /// `depositTransaction` function. This is a simple differential test. - function test_upgrade_correctEvent_succeeds(uint32 _gasLimit, bytes memory _calldata) external { - vm.assume(_calldata.length <= 120_000); - IResourceMetering.ResourceConfig memory rcfg = systemConfig.resourceConfig(); - _gasLimit = - uint32(bound(_gasLimit, optimismPortal2.minimumGasLimit(uint32(_calldata.length)), rcfg.maxResourceLimit)); - - vm.recordLogs(); - - vm.prank(superchainConfig.upgrader()); - optimismPortal2.upgrade(_gasLimit, _calldata); - - // Advance the block to ensure that the deposit transaction is processed in the next block, so that we - // aren't limited by the resource limit consumed by the previous call. - vm.roll(block.number + 1); - vm.prank(Constants.DEPOSITOR_ACCOUNT, Constants.DEPOSITOR_ACCOUNT); - optimismPortal2.depositTransaction({ - _to: Predeploys.PROXY_ADMIN, - _value: 0, - _gasLimit: uint64(_gasLimit), - _isCreation: false, - _data: _calldata + _data: abi.encodeCall(IL1Block.setGasPayingToken, (_token, 18, name, symbol)) }); VmSafe.Log[] memory logs = vm.getRecordedLogs(); @@ -444,41 +371,12 @@ contract OptimismPortal2_Test is CommonTest { assertEq(systemPath.data, userPath.data); } - /// @dev Tests that any config type can be set by the system config. - function testFuzz_setConfig_succeeds(uint8 _configType, bytes calldata _value) public { - // Ensure that _configType is within the range of the ConfigType enum - _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); - - vm.expectEmit(address(optimismPortal2)); - emitTransactionDeposited({ - _from: Constants.DEPOSITOR_ACCOUNT, - _to: Predeploys.L1_BLOCK_ATTRIBUTES, - _value: 0, - _mint: 0, - _gasLimit: 200_000, - _isCreation: false, - _data: abi.encodeCall(IL1Block.setConfig, (Types.ConfigType(_configType), _value)) - }); - - vm.prank(address(optimismPortal2.systemConfig())); - optimismPortal2.setConfig(Types.ConfigType(_configType), _value); - } - - /// @dev Tests that the gas paying token cannot be set by a non-system config for any config type. - function testFuzz_setConfig_notSystemConfig_fails( - address _caller, - uint8 _configType, - bytes calldata _value - ) - external - { - // Ensure that _configType is within the range of the ConfigType enum - _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); - + /// @dev Tests that the gas paying token cannot be set by a non-system config. + function test_setGasPayingToken_notSystemConfig_fails(address _caller) external { vm.assume(_caller != address(systemConfig)); vm.prank(_caller); vm.expectRevert(Unauthorized.selector); - optimismPortal2.setConfig(Types.ConfigType(_configType), _value); + optimismPortal2.setGasPayingToken({ _token: address(0), _decimals: 0, _name: "", _symbol: "" }); } /// @dev Tests that `depositERC20Transaction` reverts when the gas paying token is ether. diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol index a8e0344359ed..bc9a980276aa 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol @@ -7,13 +7,12 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Types } from "src/libraries/Types.sol"; import "src/libraries/PortalErrors.sol"; // Target contract dependencies import "src/libraries/PortalErrors.sol"; import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol"; -import { L1BlockInterop } from "src/L2/L1BlockInterop.sol"; +import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; // Interfaces import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; @@ -26,11 +25,31 @@ contract OptimismPortalInterop_Test is CommonTest { super.setUp(); } - /// @dev Tests that any config type can be set by the system config. - function testFuzz_setConfig_succeeds(uint8 _configType, bytes calldata _value) public { - // Ensure that _configType is within the range of the ConfigType enum - _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); + /// @dev Tests that the config for the gas paying token can be set. + function testFuzz_setConfig_gasPayingToken_succeeds(bytes calldata _value) public { + vm.expectEmit(address(optimismPortal)); + emitTransactionDeposited({ + _from: Constants.DEPOSITOR_ACCOUNT, + _to: Predeploys.L1_BLOCK_ATTRIBUTES, + _value: 0, + _mint: 0, + _gasLimit: 200_000, + _isCreation: false, + _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)) + }); + + vm.prank(address(_optimismPortalInterop().systemConfig())); + _optimismPortalInterop().setConfig(ConfigType.SET_GAS_PAYING_TOKEN, _value); + } + + /// @dev Tests that setting the gas paying token config as not the system config reverts. + function testFuzz_setConfig_gasPayingToken_notSystemConfig_reverts(bytes calldata _value) public { + vm.expectRevert(Unauthorized.selector); + _optimismPortalInterop().setConfig(ConfigType.SET_GAS_PAYING_TOKEN, _value); + } + /// @dev Tests that the config for adding a dependency can be set. + function testFuzz_setConfig_addDependency_succeeds(bytes calldata _value) public { vm.expectEmit(address(optimismPortal)); emitTransactionDeposited({ _from: Constants.DEPOSITOR_ACCOUNT, @@ -39,28 +58,40 @@ contract OptimismPortalInterop_Test is CommonTest { _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (Types.ConfigType(_configType), _value)) + _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)) }); vm.prank(address(_optimismPortalInterop().systemConfig())); - _optimismPortalInterop().setConfig(Types.ConfigType(_configType), _value); + _optimismPortalInterop().setConfig(ConfigType.ADD_DEPENDENCY, _value); } - /// @dev Tests that setting any config type as not the system config reverts. - function testFuzz_setConfig_notSystemConfig_reverts( - address _caller, - uint8 _configType, - bytes calldata _value - ) - external - { - // Ensure that _configType is within the range of the ConfigType enum - _configType = uint8(bound(uint256(_configType), 0, uint256(type(Types.ConfigType).max))); + /// @dev Tests that setting the add dependency config as not the system config reverts. + function testFuzz_setConfig_addDependency_notSystemConfig_reverts(bytes calldata _value) public { + vm.expectRevert(Unauthorized.selector); + _optimismPortalInterop().setConfig(ConfigType.ADD_DEPENDENCY, _value); + } + + /// @dev Tests that the config for removing a dependency can be set. + function testFuzz_setConfig_removeDependency_succeeds(bytes calldata _value) public { + vm.expectEmit(address(optimismPortal)); + emitTransactionDeposited({ + _from: Constants.DEPOSITOR_ACCOUNT, + _to: Predeploys.L1_BLOCK_ATTRIBUTES, + _value: 0, + _mint: 0, + _gasLimit: 200_000, + _isCreation: false, + _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)) + }); + + vm.prank(address(_optimismPortalInterop().systemConfig())); + _optimismPortalInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, _value); + } - vm.assume(_caller != address(_optimismPortalInterop().systemConfig())); - vm.prank(_caller); + /// @dev Tests that setting the remove dependency config as not the system config reverts. + function testFuzz_setConfig_removeDependency_notSystemConfig_reverts(bytes calldata _value) public { vm.expectRevert(Unauthorized.selector); - _optimismPortalInterop().setConfig(Types.ConfigType(_configType), _value); + _optimismPortalInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, _value); } /// @dev Returns the OptimismPortalInterop instance. diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index b7a76cb664bd..2772ec0c2a3c 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -13,10 +13,9 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SuperchainConfig_Init_Test is CommonTest { /// @dev Tests that initialization sets the correct values. These are defined in CommonTest.sol. - function test_initialize_succeeds() external view { + function test_initialize_unpaused_succeeds() external view { assertFalse(superchainConfig.paused()); assertEq(superchainConfig.guardian(), deploy.cfg().superchainConfigGuardian()); - assertEq(superchainConfig.upgrader(), deploy.cfg().finalSystemOwner()); } /// @dev Tests that it can be intialized as paused. @@ -37,15 +36,11 @@ contract SuperchainConfig_Init_Test is CommonTest { vm.startPrank(alice); newProxy.upgradeToAndCall( address(newImpl), - abi.encodeCall( - ISuperchainConfig.initialize, - (deploy.cfg().superchainConfigGuardian(), deploy.cfg().finalSystemOwner(), true) - ) + abi.encodeCall(ISuperchainConfig.initialize, (deploy.cfg().superchainConfigGuardian(), true)) ); assertTrue(ISuperchainConfig(address(newProxy)).paused()); assertEq(ISuperchainConfig(address(newProxy)).guardian(), deploy.cfg().superchainConfigGuardian()); - assertEq(ISuperchainConfig(address(newProxy)).upgrader(), deploy.cfg().finalSystemOwner()); } } diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index f2a605bfcd33..dff0852fb40d 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; // Testing import { CommonTest } from "test/setup/CommonTest.sol"; -import { VmSafe } from "forge-std/Vm.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -12,10 +11,6 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; -import { StaticConfig } from "src/libraries/StaticConfig.sol"; -import { Types } from "src/libraries/Types.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; -import { Bytes } from "src/libraries/Bytes.sol"; // Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; @@ -53,18 +48,18 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { /// @dev Tests that constructor sets the correct values. function test_constructor_succeeds() external view { ISystemConfig impl = ISystemConfig(systemConfigImpl); - assertEq(impl.owner(), address(0)); + assertEq(impl.owner(), address(0xdEaD)); assertEq(impl.overhead(), 0); - assertEq(impl.scalar(), 0); + assertEq(impl.scalar(), uint256(0x01) << 248); assertEq(impl.batcherHash(), bytes32(0)); - assertEq(impl.gasLimit(), 0); + assertEq(impl.gasLimit(), 1); assertEq(impl.unsafeBlockSigner(), address(0)); assertEq(impl.basefeeScalar(), 0); assertEq(impl.blobbasefeeScalar(), 0); IResourceMetering.ResourceConfig memory actual = impl.resourceConfig(); - assertEq(actual.maxResourceLimit, 0); - assertEq(actual.elasticityMultiplier, 0); - assertEq(actual.baseFeeMaxChangeDenominator, 0); + assertEq(actual.maxResourceLimit, 1); + assertEq(actual.elasticityMultiplier, 1); + assertEq(actual.baseFeeMaxChangeDenominator, 2); assertEq(actual.minimumBaseFee, 0); assertEq(actual.systemTxMaxGas, 0); assertEq(actual.maximumBaseFee, 0); @@ -83,7 +78,6 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { assertEq(decimals, 18); } - // TODO: add a reinit test to ensure calls are made to the Portal. /// @dev Tests that initialization sets the correct values. function test_initialize_succeeds() external view { assertEq(systemConfig.owner(), owner); @@ -119,54 +113,6 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init { assertEq(token, Constants.ETHER); assertEq(decimals, 18); } - - /// @dev Tests that the gas usage of `initialize` does not exceed the max resource limit. - function test_initialize_gasUsage() external { - // Wipe out the initialized slot so the proxy can be initialized again - vm.store(address(systemConfig), bytes32(0), bytes32(0)); - - vm.recordLogs(); - systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), - _basefeeScalar: basefeeScalar, - _blobbasefeeScalar: blobbasefeeScalar, - _gasLimit: gasLimit, - _config: Constants.DEFAULT_RESOURCE_CONFIG(), - _batchInbox: address(0), - _addresses: ISystemConfig.Addresses({ - l1CrossDomainMessenger: address(0), - l1ERC721Bridge: address(0), - l1StandardBridge: address(0), - disputeGameFactory: address(0), - optimismPortal: address(optimismPortal), - optimismMintableERC20Factory: address(0), - gasPayingToken: Constants.ETHER - }) - }); - - VmSafe.Log[] memory logs = vm.getRecordedLogs(); - uint64 totalGasUsed = 0; - for (uint256 i = 0; i < logs.length; i++) { - if (logs[i].topics[0] == keccak256("TransactionDeposited(address,address,uint256,bytes)")) { - // The first 32 bytes of the Data will give us the offset of the opaqueData, - // The next 32 bytes indicate the length of the opaqueData content. - // The remaining data is the opaqueData which is tightly packed. There are two - // uint256 values before the gasLimit, so we'll start at 4x32 = 128. - uint256 start = 128; - uint64 gasUsed = uint64(bytes8(Bytes.slice(logs[i].data, start, 8))); - // Assert that the expected SYSTEM_DEPOSIT_GAS_LIMIT gas limit is used for the - // detected events. - assertEq(gasUsed, 200_000); - totalGasUsed += gasUsed; - } - } - assertLe(totalGasUsed, Constants.DEFAULT_RESOURCE_CONFIG().maxResourceLimit); - } } contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { @@ -182,15 +128,12 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { vm.expectRevert("SystemConfig: gas limit too low"); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + _owner: alice, _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, + _batcherHash: bytes32(hex"abcd"), _gasLimit: minimumGasLimit - 1, + _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -198,7 +141,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(optimismPortal), + optimismPortal: address(0), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -215,15 +158,12 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { // Initialize and check that StartBlock updates to current block number vm.prank(systemConfig.owner()); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + _owner: alice, _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, + _batcherHash: bytes32(hex"abcd"), _gasLimit: gasLimit, + _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -231,7 +171,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(optimismPortal), + optimismPortal: address(0), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -249,15 +189,12 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { // Initialize and check that StartBlock doesn't update vm.prank(systemConfig.owner()); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + _owner: alice, _basefeeScalar: basefeeScalar, _blobbasefeeScalar: blobbasefeeScalar, + _batcherHash: bytes32(hex"abcd"), _gasLimit: gasLimit, + _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -265,7 +202,7 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(optimismPortal), + optimismPortal: address(0), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) @@ -347,15 +284,12 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init { vm.expectRevert(bytes(revertMessage)); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: address(0xdEaD), - feeAdmin: address(0xdEaD), - unsafeBlockSigner: address(1), - batcherHash: bytes32(0) - }), + _owner: address(0xdEaD), _basefeeScalar: 0, _blobbasefeeScalar: 0, + _batcherHash: bytes32(0), _gasLimit: gasLimit, + _unsafeBlockSigner: address(0), _config: config, _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -363,7 +297,7 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(optimismPortal), + optimismPortal: address(0), optimismMintableERC20Factory: address(0), gasPayingToken: address(0) }) @@ -389,15 +323,12 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SYMBOL_SLOT, bytes32(0)); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + _owner: alice, _basefeeScalar: 2100, _blobbasefeeScalar: 1000000, + _batcherHash: bytes32(hex"abcd"), _gasLimit: 30_000_000, + _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ @@ -410,13 +341,6 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { gasPayingToken: _gasPayingToken }) }); - - vm.roll(block.number + 1); - // Reset the OptimismPortal resource config gas used - bytes32 slot = vm.load(address(optimismPortal), bytes32(uint256(1))); - vm.store( - address(optimismPortal), bytes32(uint256(1)), bytes32(uint256(slot) & ~(uint256(type(uint64).max) << 64)) - ); } /// @dev Tests that initialization sets the correct values and getters work. @@ -527,15 +451,9 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { /// @dev Tests that initialization works with OptimismPortal. function test_initialize_customGasTokenCall_succeeds() external { - bytes memory data = StaticConfig.encodeSetGasPayingToken({ - _token: address(token), - _decimals: 18, - _name: bytes32("Silly"), - _symbol: bytes32("SIL") - }); - vm.expectCall( - address(optimismPortal), abi.encodeCall(optimismPortal.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + address(optimismPortal), + abi.encodeCall(optimismPortal.setGasPayingToken, (address(token), 18, bytes32("Silly"), bytes32("SIL"))) ); vm.expectEmit(address(optimismPortal)); @@ -548,7 +466,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { uint256(0), // value uint64(200_000), // gasLimit false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (Types.ConfigType.GAS_PAYING_TOKEN, data)) + abi.encodeCall(IL1Block.setGasPayingToken, (address(token), 18, bytes32("Silly"), bytes32("SIL"))) ) ); @@ -609,26 +527,6 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init { systemConfig.setGasLimit(maximumGasLimit + 1); } - /// @dev Tests that `setFeeVaultConfig` reverts if the config type is not a fee vault config. - function testFuzz_setFeeVaultConfig_badType_reverts(uint8 _type) external { - // Ensure that _type is a valid ConfigType, but not a fee vault type. - _type = uint8(bound(uint256(_type), 0, uint256(uint8(type(Types.ConfigType).max)))); - vm.assume(_type == 0 || _type > 3); - - vm.prank(systemConfig.feeAdmin()); - vm.expectRevert("SystemConfig: ConfigType is is not a Fee Vault Config type"); - systemConfig.setFeeVaultConfig(Types.ConfigType(_type), address(0), 0, Types.WithdrawalNetwork.L1); - } - - /// @dev Tests that `setFeeVaultConfig` reverts if the caller is not authorized. - function testFuzz_setFeeVaultConfig_badAuth_reverts(address _caller) external { - vm.assume(_caller != systemConfig.feeAdmin()); - vm.expectRevert("SystemConfig: caller is not the fee admin"); - - vm.prank(_caller); - systemConfig.setFeeVaultConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG, _caller, 0, Types.WithdrawalNetwork.L1); - } - /// @dev Tests that `setEIP1559Params` reverts if the caller is not the owner. function test_setEIP1559Params_notOwner_reverts(uint32 _denominator, uint32 _elasticity) external { vm.expectRevert("Ownable: caller is not the owner"); @@ -717,42 +615,6 @@ contract SystemConfig_Setters_Test is SystemConfig_Init { assertEq(systemConfig.unsafeBlockSigner(), newUnsafeSigner); } - /// @dev Tests that `setFeeVaultConfig` emits the expected event in the OptimismPortal - function testFuzz_setFeeVaultConfig_succeeds( - uint8 _vaultConfig, - address _recipient, - uint256 _min, - uint8 _network - ) - external - { - vm.assume(_min <= type(uint88).max); - // Bound the _vaultConfig to one of the 3 enum entries associated with Fee Vault Config. - Types.ConfigType feeType = Types.ConfigType(uint8(bound(_vaultConfig, 1, 3))); - // Bound the _network to one of the 2 enum entries associated with Withdrawal Network. - Types.WithdrawalNetwork withdrawalNetwork = Types.WithdrawalNetwork(uint8(bound(_network, 0, 1))); - - bytes memory value = abi.encode(Encoding.encodeFeeVaultConfig(_recipient, _min, withdrawalNetwork)); - - address feeAdmin = systemConfig.feeAdmin(); - vm.expectEmit(address(optimismPortal2)); - emit TransactionDeposited( - 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001, - Predeploys.L1_BLOCK_ATTRIBUTES, - 0, - abi.encodePacked( - uint256(0), // mint - uint256(0), // value - uint64(200_000), // gasLimit - false, // isCreation, - abi.encodeCall(IL1Block.setConfig, (feeType, value)) - ) - ); - - vm.prank(feeAdmin); - systemConfig.setFeeVaultConfig(feeType, _recipient, _min, withdrawalNetwork); - } - /// @dev Tests that `setEIP1559Params` updates the EIP1559 parameters successfully. function testFuzz_setEIP1559Params_succeeds(uint32 _denominator, uint32 _elasticity) external { vm.assume(_denominator > 1); @@ -769,5 +631,3 @@ contract SystemConfig_Setters_Test is SystemConfig_Init { assertEq(systemConfig.eip1559Elasticity(), _elasticity); } } - -// TODO: GasBenchmarks for initialize diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index f10d175b0d5c..426dba30c72a 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -6,12 +6,12 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; @@ -50,7 +50,7 @@ contract SystemConfigInterop_Test is CommonTest { abi.encodeCall( IOptimismPortalInterop.setConfig, ( - Types.ConfigType.GAS_PAYING_TOKEN, + ConfigType.SET_GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: 18, @@ -70,7 +70,7 @@ contract SystemConfigInterop_Test is CommonTest { address(optimismPortal), abi.encodeCall( IOptimismPortalInterop.setConfig, - (Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)) + (ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)) ) ); @@ -92,7 +92,7 @@ contract SystemConfigInterop_Test is CommonTest { address(optimismPortal), abi.encodeCall( IOptimismPortalInterop.setConfig, - (Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)) + (ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)) ) ); @@ -117,15 +117,12 @@ contract SystemConfigInterop_Test is CommonTest { vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SYMBOL_SLOT, bytes32(0)); systemConfig.initialize({ - _roles: ISystemConfig.Roles({ - owner: alice, - feeAdmin: bob, - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + _owner: alice, _basefeeScalar: 2100, _blobbasefeeScalar: 1000000, + _batcherHash: bytes32(hex"abcd"), _gasLimit: 30_000_000, + _unsafeBlockSigner: address(1), _config: Constants.DEFAULT_RESOURCE_CONFIG(), _batchInbox: address(0), _addresses: ISystemConfig.Addresses({ diff --git a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol index 520adc77c55b..bf63a700c098 100644 --- a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol @@ -8,7 +8,7 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract BaseFeeVault_Test is Bridge_Initializer { +contract FeeVault_Test is Bridge_Initializer { /// @dev Tests that the constructor sets the correct values. function test_constructor_baseFeeVault_succeeds() external view { assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol index c317519c5dbd..a80f633fede2 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol @@ -7,6 +7,7 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; import { Encoding } from "src/libraries/Encoding.sol"; +import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; // Target contract dependencies import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; @@ -38,6 +39,18 @@ contract CrossDomainOwnable2_Test is Bridge_Initializer { setter.set(1); } + /// @dev Tests that the `onlyOwner` modifier reverts when not called by the owner. + function test_onlyOwner_notOwner_reverts() external { + // set the xDomainMsgSender storage slot + bytes32 key = bytes32(uint256(204)); + bytes32 value = Bytes32AddressLib.fillLast12Bytes(address(alice)); + vm.store(address(l2CrossDomainMessenger), key, value); + + vm.prank(address(l2CrossDomainMessenger)); + vm.expectRevert("CrossDomainOwnable2: caller is not the owner"); + setter.set(1); + } + /// @dev Tests that the `onlyOwner` modifier causes the relayed message to fail. function test_onlyOwner_notOwner2_reverts() external { uint240 nonce = 0; diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol index 6c86ccb50b11..968910478198 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol @@ -71,7 +71,7 @@ contract CrossDomainOwnable3_Test is Bridge_Initializer { setter.transferOwnership({ _owner: alice, _isLocal: false }); // set the xDomainMsgSender storage slot - bytes32 key = bytes32(uint256(208)); + bytes32 key = bytes32(uint256(204)); bytes32 value = Bytes32AddressLib.fillLast12Bytes(bob); vm.store(address(l2CrossDomainMessenger), key, value); diff --git a/packages/contracts-bedrock/test/L2/FeeVault.t.sol b/packages/contracts-bedrock/test/L2/FeeVault.t.sol deleted file mode 100644 index bf63a700c098..000000000000 --- a/packages/contracts-bedrock/test/L2/FeeVault.t.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; - -// Libraries -import { Types } from "src/libraries/Types.sol"; - -// Test the implementations of the FeeVault -contract FeeVault_Test is Bridge_Initializer { - /// @dev Tests that the constructor sets the correct values. - function test_constructor_baseFeeVault_succeeds() external view { - assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); - assertEq(baseFeeVault.recipient(), deploy.cfg().baseFeeVaultRecipient()); - assertEq(baseFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); - assertEq(baseFeeVault.minWithdrawalAmount(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); - assertEq(uint8(baseFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L1)); - assertEq(uint8(baseFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); - } -} diff --git a/packages/contracts-bedrock/test/L2/L1Block.t.sol b/packages/contracts-bedrock/test/L2/L1Block.t.sol index 58fc6e08ea4e..762553a2ff2f 100644 --- a/packages/contracts-bedrock/test/L2/L1Block.t.sol +++ b/packages/contracts-bedrock/test/L2/L1Block.t.sol @@ -6,11 +6,8 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Encoding } from "src/libraries/Encoding.sol"; -import { Types } from "src/libraries/Types.sol"; -import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { Constants } from "src/libraries/Constants.sol"; import "src/libraries/L1BlockErrors.sol"; -import { LibString } from "lib/solady/src/utils/LibString.sol"; contract L1BlockTest is CommonTest { address depositor; @@ -168,148 +165,6 @@ contract L1BlockEcotone_Test is L1BlockTest { } } -contract L1BlockIsthmus_Test is L1BlockTest { - /// @dev Tests that `setIsthmus` succeeds if sender address is the depositor - function test_setIsthmus_isDepositor_succeeds() external { - vm.prank(depositor); - l1Block.setIsthmus(); - assertTrue(l1Block.isIsthmus()); - - bytes memory baseFeeVaultConfig = l1Block.getConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG); - (address recipient, uint256 min, Types.WithdrawalNetwork network) = - Encoding.decodeFeeVaultConfig(bytes32(baseFeeVaultConfig)); - assertEq(recipient, deploy.cfg().baseFeeVaultRecipient()); - assertEq(min, deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); - assertEq(uint8(network), uint8(deploy.cfg().baseFeeVaultWithdrawalNetwork())); - - bytes memory sequencerFeeVaultConfig = l1Block.getConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); - (recipient, min, network) = Encoding.decodeFeeVaultConfig(bytes32(sequencerFeeVaultConfig)); - assertEq(recipient, deploy.cfg().sequencerFeeVaultRecipient()); - assertEq(min, deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); - assertEq(uint8(network), uint8(deploy.cfg().sequencerFeeVaultWithdrawalNetwork())); - - bytes memory l1FeeVaultConfig = l1Block.getConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG); - (recipient, min, network) = Encoding.decodeFeeVaultConfig(bytes32(l1FeeVaultConfig)); - assertEq(recipient, deploy.cfg().l1FeeVaultRecipient()); - assertEq(min, deploy.cfg().l1FeeVaultMinimumWithdrawalAmount()); - assertEq(uint8(network), uint8(deploy.cfg().l1FeeVaultWithdrawalNetwork())); - - assertEq( - abi.decode(l1Block.getConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS), (address)), - address(l1ERC721Bridge) - ); - assertEq( - abi.decode(l1Block.getConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS), (address)), - address(l1CrossDomainMessenger) - ); - assertEq( - abi.decode(l1Block.getConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS), (address)), - address(l1StandardBridge) - ); - assertEq(abi.decode(l1Block.getConfig(Types.ConfigType.REMOTE_CHAIN_ID), (uint256)), deploy.cfg().l1ChainID()); - } - - /// @dev Tests that `setIsthmus` reverts if sender address is not the depositor - function test_setIsthmus_notDepositor_reverts() external { - vm.expectRevert(NotDepositor.selector); - l1Block.setIsthmus(); - } -} - -contract L1BlockConfig_Test is L1BlockTest { - /// @notice Ensures that `setConfig` always reverts when called, across all possible config types. - /// Use a magic number of 10 since solidity doesn't offer a good way to know the nubmer - /// of enum elements. - function test_setConfig_onlyDepositor_reverts(address _caller, uint8 _type) external { - vm.assume(_caller != Constants.DEPOSITOR_ACCOUNT); - vm.assume(_type < 10); // the number of defined config types - vm.expectRevert(NotDepositor.selector); - vm.prank(_caller); - l1Block.setConfig(Types.ConfigType(_type), hex""); - } - - function test_getConfigRoundtripGasPayingToken_succeeds( - address _token, - uint8 _decimals, - bytes32 _name, - bytes32 _symbol - ) - external - { - _name = LibString.normalizeSmallString(_name); - _symbol = LibString.normalizeSmallString(_symbol); - // Both the 0 address and the ether address are prevented from being set by - // `_setGasPayingToken` in `SystemConfig.sol` - vm.assume(_token != address(0) && _token != Constants.ETHER); - vm.prank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(Types.ConfigType.GAS_PAYING_TOKEN, abi.encode(_token, _decimals, _name, _symbol)); - bytes memory data = l1Block.getConfig(Types.ConfigType.GAS_PAYING_TOKEN); - (address token, uint8 decimals, bytes32 name, bytes32 symbol) = - abi.decode(data, (address, uint8, bytes32, bytes32)); - assertEq(token, _token); - assertEq(decimals, _decimals); - - assertEq(name, _name); - assertEq(symbol, _symbol); - } - - /// @notice Tests roundtrip setConfig/getConfig for base fee vault config - function test_getConfigRoundtripBaseFeeVault_succeeds(bytes32 _config) external { - _getConfigRoundTrip(_config, Types.ConfigType.BASE_FEE_VAULT_CONFIG); - } - - /// @notice Tests roundtrip setConfig/getConfig for L1 fee vault config - function test_getConfigRoundtripL1FeeVault_succeeds(bytes32 _config) external { - _getConfigRoundTrip(_config, Types.ConfigType.L1_FEE_VAULT_CONFIG); - } - - /// @notice Tests roundtrip setConfig/getConfig for sequencer fee vault config - function test_getConfigRoundtripSequencerFeeVault_succeeds(bytes32 _config) external { - _getConfigRoundTrip(_config, Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG); - } - - /// @notice Internal function for logic on round trip testing fee vault config - function _getConfigRoundTrip(bytes32 _config, Types.ConfigType _type) internal { - vm.prank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(_type, abi.encode(_config)); - bytes memory data = l1Block.getConfig(_type); - bytes32 config = abi.decode(data, (bytes32)); - assertEq(config, _config); - } - - function test_getConfigRoundtripL1CrossDomainMessenger_succeeds(address _addr) external { - _getConfigRoundTrip(_addr, Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS); - } - - function test_getConfigRoundtripL1ERC721Bridge_succeeds(address _addr) external { - _getConfigRoundTrip(_addr, Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS); - } - - function test_getConfigRoundtripL1StandardBridge_succeeds(address _addr) external { - _getConfigRoundTrip(_addr, Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS); - } - - function _getConfigRoundTrip(address _addr, Types.ConfigType _type) internal { - vm.prank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(_type, abi.encode(_addr)); - bytes memory data = l1Block.getConfig(_type); - address addr = abi.decode(data, (address)); - assertEq(addr, _addr); - } - - function test_getConfigRoundtripRemoteChainId_succeeds(uint256 _value) external { - _getConfigRoundTrip(_value, Types.ConfigType.REMOTE_CHAIN_ID); - } - - function _getConfigRoundTrip(uint256 _value, Types.ConfigType _type) internal { - vm.prank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(_type, abi.encode(_value)); - bytes memory data = l1Block.getConfig(_type); - uint256 value = abi.decode(data, (uint256)); - assertEq(value, _value); - } -} - contract L1BlockCustomGasToken_Test is L1BlockTest { function testFuzz_setGasPayingToken_succeeds( address _token, diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol index fca80975141c..6f0ef2188b8c 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol @@ -6,10 +6,9 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { StaticConfig } from "src/libraries/StaticConfig.sol"; -import { Types } from "src/libraries/Types.sol"; // Target contract dependencies -import { L1BlockInterop } from "src/L2/L1BlockInterop.sol"; +import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import "src/libraries/L1BlockErrors.sol"; @@ -35,7 +34,7 @@ contract L1BlockInteropTest is CommonTest { function testFuzz_isInDependencySet_succeeds(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); assertTrue(_l1BlockInterop().isInDependencySet(_chainId)); } @@ -71,7 +70,7 @@ contract L1BlockInteropTest is CommonTest { for (uint256 i = 0; i < _dependencySetSize; i++) { if (i == block.chainid) continue; - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); uniqueCount++; } @@ -99,7 +98,7 @@ contract L1BlockInteropTest is CommonTest { emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); _l1BlockInterop().setConfig( - Types.ConfigType.GAS_PAYING_TOKEN, + ConfigType.SET_GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }) ); } @@ -117,7 +116,7 @@ contract L1BlockInteropTest is CommonTest { vm.expectRevert(NotDepositor.selector); _l1BlockInterop().setConfig( - Types.ConfigType.GAS_PAYING_TOKEN, + ConfigType.SET_GAS_PAYING_TOKEN, StaticConfig.encodeSetGasPayingToken({ _token: _token, _decimals: _decimals, _name: _name, _symbol: _symbol }) ); } @@ -129,41 +128,41 @@ contract L1BlockInteropTest is CommonTest { vm.expectEmit(address(l1Block)); emit DependencyAdded(_chainId); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that adding a dependency reverts if it's the chain's chain id function test_setConfig_addDependency_chainChainId_reverts() public prankDepositor { vm.expectRevert(AlreadyDependency.selector); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(block.chainid)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(block.chainid)); } /// @dev Tests that adding a dependency already in the set reverts function test_setConfig_addDependency_alreadyDependency_reverts(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); vm.expectRevert(AlreadyDependency.selector); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that setting the add dependency config as not the depositor reverts. function testFuzz_setConfig_addDependency_notDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that setting the add dependency config when the dependency set size is too large reverts. function test_setConfig_addDependency_dependencySetSizeTooLarge_reverts() public prankDepositor { for (uint256 i = 0; i < type(uint8).max; i++) { - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); } assertEq(_l1BlockInterop().dependencySetSize(), type(uint8).max); vm.expectRevert(DependencySetSizeTooLarge.selector); - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(1)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(1)); } /// @dev Tests that the config for removing a dependency can be set. @@ -171,26 +170,24 @@ contract L1BlockInteropTest is CommonTest { vm.assume(_chainId != block.chainid); // Add the chain ID to the dependency set before removing it - _l1BlockInterop().setConfig(Types.ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); vm.expectEmit(address(l1Block)); emit DependencyRemoved(_chainId); - _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Tests that setting the remove dependency config as not the depositor reverts. function testFuzz_setConfig_removeDependency_notDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Tests that setting the remove dependency config for the chain's chain ID reverts. function test_setConfig_removeDependency_chainChainId_reverts() public prankDepositor { vm.expectRevert(CantRemovedDependency.selector); - _l1BlockInterop().setConfig( - Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(block.chainid) - ); + _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(block.chainid)); } /// @dev Tests that setting the remove dependency config for a chain ID that is not in the dependency set reverts. @@ -198,7 +195,7 @@ contract L1BlockInteropTest is CommonTest { vm.assume(_chainId != block.chainid); vm.expectRevert(NotDependency.selector); - _l1BlockInterop().setConfig(Types.ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); + _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Returns the L1BlockInterop instance. diff --git a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol index 825ee77e5c1f..03a3e7e5ad9b 100644 --- a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol @@ -8,7 +8,7 @@ import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract L1FeeVault_Test is Bridge_Initializer { +contract FeeVault_Test is Bridge_Initializer { /// @dev Tests that the constructor sets the correct values. function test_constructor_l1FeeVault_succeeds() external view { assertEq(l1FeeVault.RECIPIENT(), deploy.cfg().l1FeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index bd6ee0cd28b6..1dc5749d6d54 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -21,14 +21,12 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { address recipient = address(0xabbaacdc); /// @dev Tests that the implementation is initialized correctly. - /// The implementation returns the actual address rather than `address(0)` - /// because the contract calls L1Block for the result. function test_constructor_succeeds() external view { IL2CrossDomainMessenger impl = IL2CrossDomainMessenger(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2CrossDomainMessenger"))); - assertEq(address(impl.OTHER_MESSENGER()), address(l1CrossDomainMessenger)); - assertEq(address(impl.otherMessenger()), address(l1CrossDomainMessenger)); - assertEq(address(impl.l1CrossDomainMessenger()), address(l1CrossDomainMessenger)); + assertEq(address(impl.OTHER_MESSENGER()), address(0)); + assertEq(address(impl.otherMessenger()), address(0)); + assertEq(address(impl.l1CrossDomainMessenger()), address(0)); } /// @dev Tests that the proxy is initialized correctly. diff --git a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol index 54993d666a4f..58179cb93207 100644 --- a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol @@ -22,7 +22,7 @@ contract TestERC721 is ERC721 { contract TestMintableERC721 is OptimismMintableERC721 { constructor( - IL2ERC721Bridge _bridge, + address _bridge, address _remoteToken ) OptimismMintableERC721(_bridge, 1, _remoteToken, "Test", "TST") @@ -61,7 +61,7 @@ contract L2ERC721Bridge_Test is Bridge_Initializer { super.setUp(); remoteToken = new TestERC721(); - localToken = new TestMintableERC721(l2ERC721Bridge, address(remoteToken)); + localToken = new TestMintableERC721(address(l2ERC721Bridge), address(remoteToken)); // Mint alice a token. localToken.mint(alice, tokenId); diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 827849826836..57b22e628eba 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -191,7 +191,7 @@ contract L2GenesisTest is Test { // 16 prefunded dev accounts are excluded assertEq(expected, getJSONKeyCount(_path), "key count check"); - // 2 slots: implementation, admin - assertEq(2, getStorageKeysCount(_path, Predeploys.PROXY_ADMIN), "proxy admin storage check"); + // 3 slots: implementation, owner, admin + assertEq(3, getStorageKeysCount(_path, Predeploys.PROXY_ADMIN), "proxy admin storage check"); } } diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol index f4fadb06dd31..d703ec95f64e 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol @@ -28,12 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer { function test_constructor_succeeds() external view { IL2StandardBridge impl = IL2StandardBridge(payable(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2StandardBridge")))); - // The implementation contract calls out to L1Block to get the otherBridge + // The implementation contract is initialized with a 0 L1 bridge address, // but the L2 cross-domain-messenger is always set to the predeploy address for both proxy and implementation. - assertEq(address(impl.MESSENGER()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor check MESSENGER"); - assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor check messenger"); - assertEq(address(impl.OTHER_BRIDGE()), address(l1StandardBridge), "constructor check OTHER_BRIDGE"); - assertEq(address(impl.otherBridge()), address(l1StandardBridge), "constructor check otherBridge"); + assertEq(address(impl.MESSENGER()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check MESSENGER"); + assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check messenger"); + assertEq(address(impl.OTHER_BRIDGE()), address(0), "constructor zero check OTHER_BRIDGE"); + assertEq(address(impl.otherBridge()), address(0), "constructor zero check otherBridge"); } /// @dev Tests that the bridge is initialized correctly. diff --git a/packages/contracts-bedrock/test/L2/Predeploys.t.sol b/packages/contracts-bedrock/test/L2/Predeploys.t.sol index 38e8ff74be69..27e3d75b2fdc 100644 --- a/packages/contracts-bedrock/test/L2/Predeploys.t.sol +++ b/packages/contracts-bedrock/test/L2/Predeploys.t.sol @@ -21,9 +21,10 @@ contract PredeploysBaseTest is CommonTest { return _addr == Predeploys.L1_MESSAGE_SENDER; } - /// @dev No predeploys should ever be initializable. - function _isInitializable(address) internal pure returns (bool) { - return false; + /// @dev Returns true if the predeploy is initializable. + function _isInitializable(address _addr) internal pure returns (bool) { + return _addr == Predeploys.L2_CROSS_DOMAIN_MESSENGER || _addr == Predeploys.L2_STANDARD_BRIDGE + || _addr == Predeploys.L2_ERC721_BRIDGE || _addr == Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY; } /// @dev Returns true if the predeploy uses immutables. @@ -48,7 +49,7 @@ contract PredeploysBaseTest is CommonTest { ); } - function _test_predeploys(bool _useInterop) internal view { + function _test_predeploys(bool _useInterop) internal { uint256 count = 2048; uint160 prefix = uint160(0x420) << 148; @@ -103,6 +104,10 @@ contract PredeploysBaseTest is CommonTest { // can't check bytecode if it's modified with immutables in genesis. assertEq(implAddr.code, supposedCode, "proxy implementation contract should match contract source"); } + + if (_isInitializable(addr)) { + assertEq(l2Genesis.loadInitializedSlot(cname), uint8(1)); + } } } } @@ -110,7 +115,7 @@ contract PredeploysBaseTest is CommonTest { contract PredeploysTest is PredeploysBaseTest { /// @dev Tests that the predeploy addresses are set correctly. They have code /// and the proxied accounts have the correct admin. - function test_predeploys_succeeds() external view { + function test_predeploys_succeeds() external { _test_predeploys(false); } } @@ -124,7 +129,7 @@ contract PredeploysInteropTest is PredeploysBaseTest { /// @dev Tests that the predeploy addresses are set correctly. They have code /// and the proxied accounts have the correct admin. Using interop. - function test_predeploys_succeeds() external view { + function test_predeploys_succeeds() external { _test_predeploys(true); } } diff --git a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol index 9ba232dcf0b3..ca8c3806b38b 100644 --- a/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/SequencerFeeVault.t.sol @@ -4,29 +4,33 @@ pragma solidity 0.8.15; // Testing import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; +import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; + +// Contracts +import { ISequencerFeeVault } from "src/L2/interfaces/ISequencerFeeVault.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; import { Types } from "src/libraries/Types.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; -import { Constants } from "src/libraries/Constants.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract SequencerFeeVault_Test is CommonTest { + address recipient; + + /// @dev Sets up the test suite. function setUp() public override { super.setUp(); - assertEq(uint8(deploy.cfg().sequencerFeeVaultWithdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); + recipient = deploy.cfg().sequencerFeeVaultRecipient(); } - /// @dev Tests that the sequencer fee wallet is correct. + /// @dev Tests that the l1 fee wallet is correct. function test_constructor_succeeds() external view { - address recipient = deploy.cfg().sequencerFeeVaultRecipient(); - uint256 amount = deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(); assertEq(sequencerFeeVault.l1FeeWallet(), recipient); assertEq(sequencerFeeVault.RECIPIENT(), recipient); assertEq(sequencerFeeVault.recipient(), recipient); - assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), amount); - assertEq(sequencerFeeVault.minWithdrawalAmount(), amount); + assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); + assertEq(sequencerFeeVault.minWithdrawalAmount(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount()); assertEq(uint8(sequencerFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L1)); assertEq(uint8(sequencerFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L1)); } @@ -59,8 +63,6 @@ contract SequencerFeeVault_Test is CommonTest { // No ether has been withdrawn yet assertEq(sequencerFeeVault.totalProcessed(), 0); - address recipient = deploy.cfg().sequencerFeeVaultRecipient(); - vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); emit Withdrawal(address(sequencerFeeVault).balance, recipient, address(this)); vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); @@ -100,31 +102,34 @@ contract SequencerFeeVault_Test is CommonTest { } contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { + /// @dev a cache for the config fee recipient + address recipient; + /// @dev Sets up the test suite. function setUp() public override { super.setUp(); - // Explicitly use L2 withdrawal network - bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: deploy.cfg().sequencerFeeVaultRecipient(), - _amount: deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork.L2 - }); - vm.prank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig)); - } + // Alter the deployment to use WithdrawalNetwork.L2 + vm.etch( + EIP1967Helper.getImplementation(Predeploys.SEQUENCER_FEE_WALLET), + address( + DeployUtils.create1({ + _name: "SequencerFeeVault", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + ISequencerFeeVault.__constructor__, + ( + deploy.cfg().sequencerFeeVaultRecipient(), + deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), + Types.WithdrawalNetwork.L2 + ) + ) + ) + }) + ).code + ); - /// @dev Tests that the sequencer fee wallet is correct. - function test_constructor_succeeds() external view { - address recipient = deploy.cfg().sequencerFeeVaultRecipient(); - uint256 amount = deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(); - assertEq(sequencerFeeVault.l1FeeWallet(), recipient); - assertEq(sequencerFeeVault.RECIPIENT(), recipient); - assertEq(sequencerFeeVault.recipient(), recipient); - assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), amount); - assertEq(sequencerFeeVault.minWithdrawalAmount(), amount); - assertEq(uint8(sequencerFeeVault.WITHDRAWAL_NETWORK()), uint8(Types.WithdrawalNetwork.L2)); - assertEq(uint8(sequencerFeeVault.withdrawalNetwork()), uint8(Types.WithdrawalNetwork.L2)); + recipient = deploy.cfg().sequencerFeeVaultRecipient(); } /// @dev Tests that `withdraw` successfully initiates a withdrawal to L2. @@ -143,14 +148,14 @@ contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { ); // The entire vault's balance is withdrawn - vm.expectCall(sequencerFeeVault.RECIPIENT(), address(sequencerFeeVault).balance, bytes("")); + vm.expectCall(recipient, address(sequencerFeeVault).balance, bytes("")); sequencerFeeVault.withdraw(); // The withdrawal was successful assertEq(sequencerFeeVault.totalProcessed(), amount); assertEq(address(sequencerFeeVault).balance, 0); - assertEq(sequencerFeeVault.recipient().balance, amount); + assertEq(recipient.balance, amount); } /// @dev Tests that `withdraw` fails if the Recipient reverts. This also serves to simulate @@ -166,7 +171,7 @@ contract SequencerFeeVault_L2Withdrawal_Test is CommonTest { vm.etch(sequencerFeeVault.RECIPIENT(), type(Reverter).runtimeCode); // The entire vault's balance is withdrawn - vm.expectCall(sequencerFeeVault.recipient(), address(sequencerFeeVault).balance, bytes("")); + vm.expectCall(recipient, address(sequencerFeeVault).balance, bytes("")); vm.expectRevert("FeeVault: failed to send ETH to L2 fee recipient"); sequencerFeeVault.withdraw(); assertEq(sequencerFeeVault.totalProcessed(), 0); diff --git a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol index 31700f3a7529..5b0b300abda0 100644 --- a/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/invariants/SystemConfig.t.sol @@ -7,12 +7,6 @@ import { IProxy } from "src/universal/interfaces/IProxy.sol"; import { Constants } from "src/libraries/Constants.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; -contract ConfigSetter { - function setConfig(uint8, bytes calldata) external { - // noop - } -} - contract SystemConfig_GasLimitBoundaries_Invariant is Test { ISystemConfig public config; @@ -29,7 +23,6 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) }) ); - ConfigSetter setter = new ConfigSetter(); vm.prank(msg.sender); proxy.upgradeToAndCall( @@ -37,15 +30,12 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { abi.encodeCall( configImpl.initialize, ( - ISystemConfig.Roles({ - owner: address(0xbeef), - feeAdmin: address(0xbeef), - unsafeBlockSigner: address(1), - batcherHash: bytes32(hex"abcd") - }), + address(0xbeef), // owner 2100, // overhead 1000000, // scalar + bytes32(hex"abcd"), // batcher hash 30_000_000, // gas limit + address(1), // unsafe block signer Constants.DEFAULT_RESOURCE_CONFIG(), address(0), // _batchInbox ISystemConfig.Addresses({ // _addrs @@ -53,7 +43,7 @@ contract SystemConfig_GasLimitBoundaries_Invariant is Test { l1ERC721Bridge: address(0), l1StandardBridge: address(0), disputeGameFactory: address(0), - optimismPortal: address(setter), + optimismPortal: address(0), optimismMintableERC20Factory: address(0), gasPayingToken: Constants.ETHER }) diff --git a/packages/contracts-bedrock/test/libraries/Encoding.t.sol b/packages/contracts-bedrock/test/libraries/Encoding.t.sol index 11df7eeae17e..a301fdd97b3a 100644 --- a/packages/contracts-bedrock/test/libraries/Encoding.t.sol +++ b/packages/contracts-bedrock/test/libraries/Encoding.t.sol @@ -93,15 +93,4 @@ contract Encoding_Test is CommonTest { assertEq(txn, _txn); } - - // using a bool simulates the 2 enum values that exist - function testFuzz_encodeFeeVaultConfig_succeeds(address _recipient, uint88 _amount, bool _network) public pure { - Types.WithdrawalNetwork _withdrawalNetwork = _network ? Types.WithdrawalNetwork.L1 : Types.WithdrawalNetwork.L2; - bytes32 encoded = Encoding.encodeFeeVaultConfig(_recipient, uint256(_amount), _withdrawalNetwork); - (address recipient, uint256 amount, Types.WithdrawalNetwork withdrawalNetwork) = - Encoding.decodeFeeVaultConfig(encoded); - assertEq(_recipient, recipient, "bad recipient"); - assertEq(uint256(_amount), amount, "bad amount"); - assertEq(uint256(withdrawalNetwork), uint256(withdrawalNetwork), "bad network"); - } } diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 6f1f59c9fe96..54e0e456def3 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -35,7 +35,6 @@ contract DeployOPChainInput_Test is Test { // Define defaults. address opChainProxyAdminOwner = makeAddr("opChainProxyAdminOwner"); address systemConfigOwner = makeAddr("systemConfigOwner"); - address systemConfigFeeAdmin = makeAddr("systemConfigFeeAdmin"); address batcher = makeAddr("batcher"); address unsafeBlockSigner = makeAddr("unsafeBlockSigner"); address proposer = makeAddr("proposer"); @@ -53,7 +52,6 @@ contract DeployOPChainInput_Test is Test { function test_set_succeeds() public { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); - doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); @@ -69,7 +67,6 @@ contract DeployOPChainInput_Test is Test { // Compare the default inputs to the getter methods. assertEq(opChainProxyAdminOwner, doi.opChainProxyAdminOwner(), "200"); assertEq(systemConfigOwner, doi.systemConfigOwner(), "300"); - assertEq(systemConfigFeeAdmin, doi.systemConfigFeeAdmin(), "310"); assertEq(batcher, doi.batcher(), "400"); assertEq(unsafeBlockSigner, doi.unsafeBlockSigner(), "500"); assertEq(proposer, doi.proposer(), "600"); @@ -90,9 +87,6 @@ contract DeployOPChainInput_Test is Test { vm.expectRevert(expectedErr); doi.systemConfigOwner(); - vm.expectRevert(expectedErr); - doi.systemConfigFeeAdmin(); - vm.expectRevert(expectedErr); doi.batcher(); @@ -336,7 +330,6 @@ contract DeployOPChain_TestBase is Test { // `opcm` is set during `setUp` since it is an output of the previous step. address opChainProxyAdminOwner = makeAddr("defaultOPChainProxyAdminOwner"); address systemConfigOwner = makeAddr("defaultSystemConfigOwner"); - address systemConfigFeeAdmin = makeAddr("defaultSystemConfigFeeAdmin"); address batcher = makeAddr("defaultBatcher"); address unsafeBlockSigner = makeAddr("defaultUnsafeBlockSigner"); address proposer = makeAddr("defaultProposer"); @@ -437,14 +430,13 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { function testFuzz_run_memory_succeed(bytes32 _seed) public { opChainProxyAdminOwner = address(uint160(uint256(hash(_seed, 0)))); systemConfigOwner = address(uint160(uint256(hash(_seed, 1)))); - systemConfigFeeAdmin = address(uint160(uint256(hash(_seed, 2)))); - batcher = address(uint160(uint256(hash(_seed, 3)))); - unsafeBlockSigner = address(uint160(uint256(hash(_seed, 4)))); - proposer = address(uint160(uint256(hash(_seed, 5)))); - challenger = address(uint160(uint256(hash(_seed, 6)))); - basefeeScalar = uint32(uint256(hash(_seed, 7))); - blobBaseFeeScalar = uint32(uint256(hash(_seed, 8))); - l2ChainId = uint256(hash(_seed, 9)); + batcher = address(uint160(uint256(hash(_seed, 2)))); + unsafeBlockSigner = address(uint160(uint256(hash(_seed, 3)))); + proposer = address(uint160(uint256(hash(_seed, 4)))); + challenger = address(uint160(uint256(hash(_seed, 5)))); + basefeeScalar = uint32(uint256(hash(_seed, 6))); + blobBaseFeeScalar = uint32(uint256(hash(_seed, 7))); + l2ChainId = uint256(hash(_seed, 8)); // Set the initial anchor states. The typical usage we expect is to pass in one root per game type. uint256 cannonBlock = uint256(hash(_seed, 9)); @@ -467,7 +459,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); - doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); @@ -492,7 +483,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { // Assert that individual input fields were properly set based on the inputs. assertEq(opChainProxyAdminOwner, doi.opChainProxyAdminOwner(), "100"); assertEq(systemConfigOwner, doi.systemConfigOwner(), "200"); - assertEq(systemConfigFeeAdmin, doi.systemConfigFeeAdmin(), "210"); assertEq(batcher, doi.batcher(), "300"); assertEq(unsafeBlockSigner, doi.unsafeBlockSigner(), "400"); assertEq(proposer, doi.proposer(), "500"); @@ -512,7 +502,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { // Assert inputs were properly passed through to the contract initializers. assertEq(address(doo.opChainProxyAdmin().owner()), opChainProxyAdminOwner, "2100"); assertEq(address(doo.systemConfigProxy().owner()), systemConfigOwner, "2200"); - assertEq(address(doo.systemConfigProxy().feeAdmin()), systemConfigFeeAdmin, "2210"); address batcherActual = address(uint160(uint256(doo.systemConfigProxy().batcherHash()))); assertEq(batcherActual, batcher, "2300"); assertEq(address(doo.systemConfigProxy().unsafeBlockSigner()), unsafeBlockSigner, "2400"); @@ -563,7 +552,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { function setDOI() internal { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); - doi.set(doi.systemConfigFeeAdmin.selector, systemConfigFeeAdmin); doi.set(doi.batcher.selector, batcher); doi.set(doi.unsafeBlockSigner.selector, unsafeBlockSigner); doi.set(doi.proposer.selector, proposer); diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index e324aec5f3b5..ef2b654b2410 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -15,9 +15,6 @@ import { OutputMode, Fork, ForkUtils } from "scripts/libraries/Config.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; -import { Constants } from "src/libraries/Constants.sol"; -import { Encoding } from "src/libraries/Encoding.sol"; -import { Types } from "src/libraries/Types.sol"; // Interfaces import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; @@ -30,7 +27,7 @@ import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityC import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; -import { IOptimismMintableERC721Factory } from "src/L2/interfaces/IOptimismMintableERC721Factory.sol"; +import { IOptimismMintableERC721Factory } from "src/universal/interfaces/IOptimismMintableERC721Factory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; @@ -38,8 +35,7 @@ import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMesseng import { IL2StandardBridgeInterop } from "src/L2/interfaces/IL2StandardBridgeInterop.sol"; import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; import { IL2ERC721Bridge } from "src/L2/interfaces/IL2ERC721Bridge.sol"; -import { IL1OptimismMintableERC20Factory } from "src/L1/interfaces/IL1OptimismMintableERC20Factory.sol"; -import { IL2OptimismMintableERC20Factory } from "src/L2/interfaces/IL2OptimismMintableERC20Factory.sol"; +import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol"; import { IBaseFeeVault } from "src/L2/interfaces/IBaseFeeVault.sol"; @@ -87,7 +83,7 @@ contract Setup { IL1CrossDomainMessenger l1CrossDomainMessenger; IAddressManager addressManager; IL1ERC721Bridge l1ERC721Bridge; - IL1OptimismMintableERC20Factory l1OptimismMintableERC20Factory; + IOptimismMintableERC20Factory l1OptimismMintableERC20Factory; IProtocolVersions protocolVersions; ISuperchainConfig superchainConfig; IDataAvailabilityChallenge dataAvailabilityChallenge; @@ -97,8 +93,8 @@ contract Setup { IL2CrossDomainMessenger(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER)); IL2StandardBridgeInterop l2StandardBridge = IL2StandardBridgeInterop(payable(Predeploys.L2_STANDARD_BRIDGE)); IL2ToL1MessagePasser l2ToL1MessagePasser = IL2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)); - IL2OptimismMintableERC20Factory l2OptimismMintableERC20Factory = - IL2OptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); + IOptimismMintableERC20Factory l2OptimismMintableERC20Factory = + IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); IL2ERC721Bridge l2ERC721Bridge = IL2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE); IOptimismMintableERC721Factory l2OptimismMintableERC721Factory = IOptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); @@ -158,7 +154,7 @@ contract Setup { addressManager = IAddressManager(deploy.mustGetAddress("AddressManager")); l1ERC721Bridge = IL1ERC721Bridge(deploy.mustGetAddress("L1ERC721BridgeProxy")); l1OptimismMintableERC20Factory = - IL1OptimismMintableERC20Factory(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy")); + IOptimismMintableERC20Factory(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy")); protocolVersions = IProtocolVersions(deploy.mustGetAddress("ProtocolVersionsProxy")); superchainConfig = ISuperchainConfig(deploy.mustGetAddress("SuperchainConfigProxy")); anchorStateRegistry = IAnchorStateRegistry(deploy.mustGetAddress("AnchorStateRegistryProxy")); @@ -178,8 +174,8 @@ contract Setup { vm.label(address(addressManager), "AddressManager"); vm.label(address(l1ERC721Bridge), "L1ERC721Bridge"); vm.label(deploy.mustGetAddress("L1ERC721BridgeProxy"), "L1ERC721BridgeProxy"); - vm.label(address(l1OptimismMintableERC20Factory), "L1OptimismMintableERC20Factory"); - vm.label(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy"), "L1OptimismMintableERC20FactoryProxy"); + vm.label(address(l1OptimismMintableERC20Factory), "OptimismMintableERC20Factory"); + vm.label(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy"), "OptimismMintableERC20FactoryProxy"); vm.label(address(protocolVersions), "ProtocolVersions"); vm.label(deploy.mustGetAddress("ProtocolVersionsProxy"), "ProtocolVersionsProxy"); vm.label(address(superchainConfig), "SuperchainConfig"); @@ -204,16 +200,15 @@ contract Setup { /// @dev Sets up the L2 contracts. Depends on `L1()` being called first. function L2() public { console.log("Setup: creating L2 genesis with fork %s", l2Fork.toString()); - l2Genesis.runWithOptions({ - _mode: OutputMode.NONE, - _fork: l2Fork, - _populateNetworkConfig: false, - _l1Dependencies: L1Dependencies({ + l2Genesis.runWithOptions( + OutputMode.NONE, + l2Fork, + L1Dependencies({ l1CrossDomainMessengerProxy: payable(address(l1CrossDomainMessenger)), l1StandardBridgeProxy: payable(address(l1StandardBridge)), l1ERC721BridgeProxy: payable(address(l1ERC721Bridge)) }) - }); + ); // Set the governance token's owner to be the final system owner address finalSystemOwner = deploy.cfg().finalSystemOwner(); @@ -221,46 +216,8 @@ contract Setup { governanceToken.transferOwnership(finalSystemOwner); vm.stopPrank(); - // These calls by the depositor account simulate the SystemConfig setting the - // network specific configuration into L2. Ideally there is a library that automatically - // translates TransactionDeposited and ConfigUpdate events into the appropriate calls - vm.startPrank(Constants.DEPOSITOR_ACCOUNT); - l1Block.setConfig(Types.ConfigType.L1_ERC_721_BRIDGE_ADDRESS, abi.encode(l1ERC721Bridge)); - l1Block.setConfig(Types.ConfigType.REMOTE_CHAIN_ID, abi.encode(deploy.cfg().l1ChainID())); - l1Block.setConfig(Types.ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS, abi.encode(l1CrossDomainMessenger)); - l1Block.setConfig(Types.ConfigType.L1_STANDARD_BRIDGE_ADDRESS, abi.encode(l1StandardBridge)); - - bytes32 sequencerFeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: deploy.cfg().sequencerFeeVaultRecipient(), - _amount: deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(deploy.cfg().sequencerFeeVaultWithdrawalNetwork()) - }); - l1Block.setConfig(Types.ConfigType.SEQUENCER_FEE_VAULT_CONFIG, abi.encode(sequencerFeeVaultConfig)); - - bytes32 baseFeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: deploy.cfg().baseFeeVaultRecipient(), - _amount: deploy.cfg().baseFeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(deploy.cfg().baseFeeVaultWithdrawalNetwork()) - }); - l1Block.setConfig(Types.ConfigType.BASE_FEE_VAULT_CONFIG, abi.encode(baseFeeVaultConfig)); - - bytes32 l1FeeVaultConfig = Encoding.encodeFeeVaultConfig({ - _recipient: deploy.cfg().l1FeeVaultRecipient(), - _amount: deploy.cfg().l1FeeVaultMinimumWithdrawalAmount(), - _network: Types.WithdrawalNetwork(deploy.cfg().l1FeeVaultWithdrawalNetwork()) - }); - l1Block.setConfig(Types.ConfigType.L1_FEE_VAULT_CONFIG, abi.encode(l1FeeVaultConfig)); - vm.stopPrank(); - - // Reset the ResourceConfig gas used to 0 - bytes32 slot = vm.load(address(optimismPortal), bytes32(uint256(1))); - slot = bytes32(uint256(slot) & ~(uint256(type(uint64).max) << 128)); - vm.store(address(optimismPortal), bytes32(uint256(1)), slot); - vm.store(address(optimismPortal2), bytes32(uint256(1)), slot); - // L2 predeploys labelPredeploy(Predeploys.L2_STANDARD_BRIDGE); - labelPredeploy(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); labelPredeploy(Predeploys.L2_CROSS_DOMAIN_MESSENGER); labelPredeploy(Predeploys.L2_TO_L1_MESSAGE_PASSER); labelPredeploy(Predeploys.SEQUENCER_FEE_WALLET); @@ -275,7 +232,6 @@ contract Setup { labelPredeploy(Predeploys.EAS); labelPredeploy(Predeploys.SCHEMA_REGISTRY); labelPredeploy(Predeploys.WETH); - labelPredeploy(Predeploys.L2_ERC721_BRIDGE); labelPredeploy(Predeploys.SUPERCHAIN_WETH); labelPredeploy(Predeploys.ETH_LIQUIDITY); labelPredeploy(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY); @@ -303,11 +259,7 @@ contract Setup { } function labelPredeploy(address _addr) internal { - string memory name = Predeploys.getName(_addr); - vm.label(_addr, name); - if (!Predeploys.notProxied(_addr)) { - vm.label(Predeploys.predeployToCodeNamespace(_addr), string.concat(name, "Implementation")); - } + vm.label(_addr, Predeploys.getName(_addr)); } function labelPreinstall(address _addr) internal { diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol index 6b382eab117f..d146b050f387 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol @@ -12,11 +12,19 @@ import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC2 // Interfaces import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; contract OptimismMintableTokenFactory_Test is Bridge_Initializer { event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); + /// @notice Tests that the constructor is initialized correctly. + function test_constructor_succeeds() external { + IOptimismMintableERC20Factory impl = IOptimismMintableERC20Factory(address(new OptimismMintableERC20Factory())); + assertEq(address(impl.BRIDGE()), address(0)); + assertEq(address(impl.bridge()), address(0)); + } + /// @notice Tests that the proxy is initialized correctly. function test_initialize_succeeds() external view { assertEq(address(l1OptimismMintableERC20Factory.BRIDGE()), address(l1StandardBridge)); @@ -25,7 +33,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer { /// @notice Tests that the upgrade is successful. function test_upgrading_succeeds() external { - IProxy proxy = IProxy(deploy.mustGetAddress("L1OptimismMintableERC20FactoryProxy")); + IProxy proxy = IProxy(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy")); // Check an unused slot before upgrading. bytes32 slot21Before = vm.load(address(l1OptimismMintableERC20Factory), bytes32(uint256(21))); assertEq(bytes32(0), slot21Before); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol index 989699987219..daea00064cf4 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol @@ -23,7 +23,7 @@ contract OptimismMintableERC721_Test is Bridge_Initializer { // Set up the token pair. L1NFT = new ERC721("L1NFT", "L1T"); - L2NFT = new OptimismMintableERC721(l2ERC721Bridge, 1, address(L1NFT), "L2NFT", "L2T"); + L2NFT = new OptimismMintableERC721(address(l2ERC721Bridge), 1, address(L1NFT), "L2NFT", "L2T"); // Label the addresses for nice traces. vm.label(address(L1NFT), "L1ERC721Token"); @@ -34,10 +34,10 @@ contract OptimismMintableERC721_Test is Bridge_Initializer { assertEq(L2NFT.name(), "L2NFT"); assertEq(L2NFT.symbol(), "L2T"); assertEq(L2NFT.remoteToken(), address(L1NFT)); - assertEq(address(L2NFT.bridge()), address(l2ERC721Bridge)); + assertEq(L2NFT.bridge(), address(l2ERC721Bridge)); assertEq(L2NFT.remoteChainId(), 1); assertEq(L2NFT.REMOTE_TOKEN(), address(L1NFT)); - assertEq(address(L2NFT.BRIDGE()), address(l2ERC721Bridge)); + assertEq(L2NFT.BRIDGE(), address(l2ERC721Bridge)); assertEq(L2NFT.REMOTE_CHAIN_ID(), 1); } diff --git a/packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol similarity index 89% rename from packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol rename to packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol index f5256cb73cbb..ef9019eafa04 100644 --- a/packages/contracts-bedrock/test/L2/OptimismMintableERC721Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol @@ -4,15 +4,16 @@ pragma solidity 0.8.15; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; +import { OptimismMintableERC721Factory } from "src/universal/OptimismMintableERC721Factory.sol"; contract OptimismMintableERC721Factory_Test is Bridge_Initializer { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); function test_constructor_succeeds() external view { - assertEq(address(l2OptimismMintableERC721Factory.BRIDGE()), address(l2ERC721Bridge)); - assertEq(address(l2OptimismMintableERC721Factory.bridge()), address(l2ERC721Bridge)); + assertEq(l2OptimismMintableERC721Factory.BRIDGE(), address(l2ERC721Bridge)); + assertEq(l2OptimismMintableERC721Factory.bridge(), address(l2ERC721Bridge)); assertEq(l2OptimismMintableERC721Factory.REMOTE_CHAIN_ID(), deploy.cfg().l1ChainID()); - assertEq(l2OptimismMintableERC721Factory.remoteChainId(), deploy.cfg().l1ChainID()); + assertEq(l2OptimismMintableERC721Factory.remoteChainID(), deploy.cfg().l1ChainID()); } function test_createOptimismMintableERC721_succeeds() external { @@ -39,7 +40,7 @@ contract OptimismMintableERC721Factory_Test is Bridge_Initializer { assertEq(created.name(), "L2Token"); assertEq(created.symbol(), "L2T"); assertEq(created.REMOTE_TOKEN(), remote); - assertEq(address(created.BRIDGE()), address(l2ERC721Bridge)); + assertEq(created.BRIDGE(), address(l2ERC721Bridge)); assertEq(created.REMOTE_CHAIN_ID(), deploy.cfg().l1ChainID()); } diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index ea53608a7a8e..9c22e178a5f4 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -14,6 +14,7 @@ import { OPContractsManager } from "src/L1/OPContractsManager.sol"; // Interfaces import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; +import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; @@ -41,8 +42,7 @@ contract Specification_Test is CommonTest { DELAYEDWETHOWNER, COUNCILSAFE, COUNCILSAFEOWNER, - DEPENDENCYMANAGER, - FEEADMIN + DEPENDENCYMANAGER } /// @notice Represents the specification of a function. @@ -276,7 +276,7 @@ contract Specification_Test is CommonTest { _name: "OptimismPortal", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortal", _sel: _getSel("setConfig(uint8,bytes)") }); + _addSpec({ _name: "OptimismPortal", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); // OptimismPortalInterop _addSpec({ @@ -335,8 +335,12 @@ contract Specification_Test is CommonTest { _name: "OptimismPortalInterop", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("upgrade(uint32,bytes)"), _auth: Role.FEEADMIN }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("setConfig(uint8,bytes)") }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); + _addSpec({ + _name: "OptimismPortalInterop", + _sel: IOptimismPortalInterop.setConfig.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); // OptimismPortal2 _addSpec({ _name: "OptimismPortal2", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); @@ -383,27 +387,7 @@ contract Specification_Test is CommonTest { _name: "OptimismPortal2", _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(uint32,bytes)"), _auth: Role.FEEADMIN }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("setConfig(uint8,bytes)") }); - - // L1OptimismMintableERC20Factory - _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("BRIDGE()") }); - _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("bridge()") }); - _addSpec({ - _name: "L1OptimismMintableERC20Factory", - _sel: _getSel("createOptimismMintableERC20(address,string,string)") - }); - _addSpec({ - _name: "L1OptimismMintableERC20Factory", - _sel: _getSel("createOptimismMintableERC20WithDecimals(address,string,string,uint8)") - }); - _addSpec({ - _name: "L1OptimismMintableERC20Factory", - _sel: _getSel("createStandardL2Token(address,string,string)") - }); - _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("deployments(address)") }); - _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("version()") }); - _addSpec({ _name: "L1OptimismMintableERC20Factory", _sel: _getSel("initialize(address)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); // ProtocolVersions _addSpec({ _name: "ProtocolVersions", _sel: _getSel("RECOMMENDED_SLOT()") }); @@ -434,13 +418,11 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SuperchainConfig", _sel: _getSel("GUARDIAN_SLOT()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("PAUSED_SLOT()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("guardian()") }); - _addSpec({ _name: "SuperchainConfig", _sel: _getSel("initialize(address,address,bool)") }); + _addSpec({ _name: "SuperchainConfig", _sel: _getSel("initialize(address,bool)") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("pause(string)"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("paused()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("unpause()"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("version()") }); - _addSpec({ _name: "SuperchainConfig", _sel: _getSel("UPGRADER_SLOT()") }); - _addSpec({ _name: "SuperchainConfig", _sel: _getSel("upgrader()") }); // SystemConfig _addSpec({ _name: "SystemConfig", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); @@ -454,7 +436,6 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: ISystemConfig.minimumGasLimit.selector }); _addSpec({ _name: "SystemConfig", _sel: _getSel("overhead()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("owner()") }); - _addSpec({ _name: "SystemConfig", _sel: _getSel("feeAdmin()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfig", _sel: ISystemConfig.resourceConfig.selector }); _addSpec({ _name: "SystemConfig", _sel: _getSel("scalar()") }); @@ -497,7 +478,6 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: _getSel("basefeeScalar()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("blobbasefeeScalar()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("maximumGasLimit()") }); - _addSpec({ _name: "SystemConfig", _sel: _getSel("setFeeVaultConfig(uint8,address,uint256,uint8)") }); // SystemConfigInterop _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); @@ -509,15 +489,8 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("eip1559Elasticity()") }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.initialize.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.minimumGasLimit.selector }); - _addSpec({ - _name: "SystemConfigInterop", - _sel: _getSel( - "initialize((address,address,address,bytes32),uint32,uint32,uint64,(uint32,uint8,uint8,uint32,uint32,uint128),address,(address,address,address,address,address,address,address),address)" - ) - }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("overhead()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("owner()") }); - _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("feeAdmin()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.resourceConfig.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("scalar()") }); @@ -587,7 +560,12 @@ contract Specification_Test is CommonTest { _auth: Role.DEPENDENCYMANAGER }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("dependencyManager()") }); - _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("setFeeVaultConfig(uint8,address,uint256,uint8)") }); + _addSpec({ + _name: "SystemConfigInterop", + _sel: _getSel( + "initialize(address,uint32,uint32,bytes32,uint64,address,(uint32,uint8,uint8,uint32,uint32,uint128),address,(address,address,address,address,address,address,address),address)" + ) + }); // ProxyAdmin _addSpec({ _name: "ProxyAdmin", _sel: _getSel("addressManager()") }); diff --git a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol index 22146147f129..be7f8a51107c 100644 --- a/packages/contracts-bedrock/test/universal/StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/universal/StandardBridge.t.sol @@ -2,12 +2,10 @@ pragma solidity 0.8.15; import { StandardBridge } from "src/universal/StandardBridge.sol"; -import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { OptimismMintableERC20, ILegacyMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; /// @title StandardBridgeTester /// @notice Simple wrapper around the StandardBridge contract that exposes @@ -23,14 +21,6 @@ contract StandardBridgeTester is StandardBridge { return _isCorrectTokenPair(_mintableToken, _otherToken); } - function otherBridge() public pure override returns (IStandardBridge) { - return IStandardBridge(payable(address(0))); - } - - function messenger() public pure override returns (ICrossDomainMessenger) { - return ICrossDomainMessenger(address(0)); - } - function gasPayingToken() internal pure override returns (address, uint8) { return (Constants.ETHER, 18); } diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 331b141bd124..5c17987c471d 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -28,10 +28,6 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr /// deepest contract in the inheritance chain for setting up the system contracts. /// For each L1 contract both the implementation and the proxy are tested. contract Initializer_Test is Bridge_Initializer { - /// @notice Error used by openzeppelin v5 initializable. - /// The contract is already initialized. - error InvalidInitialization(); - /// @notice Contains the address of an `Initializable` contract and the calldata /// used to initialize it. struct InitializeableContract { @@ -61,7 +57,7 @@ contract Initializer_Test is Bridge_Initializer { InitializeableContract({ name: "SuperchainConfig", target: deploy.mustGetAddress("SuperchainConfig"), - initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), address(0), false)) + initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), false)) }) ); // SuperchainConfigProxy @@ -69,7 +65,7 @@ contract Initializer_Test is Bridge_Initializer { InitializeableContract({ name: "SuperchainConfigProxy", target: address(superchainConfig), - initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), address(0), false)) + initCalldata: abi.encodeCall(superchainConfig.initialize, (address(0), false)) }) ); // L1CrossDomainMessengerImpl @@ -180,15 +176,12 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall( systemConfig.initialize, ( - ISystemConfig.Roles({ - owner: address(0xdead), - feeAdmin: address(0xdead), - unsafeBlockSigner: address(0), - batcherHash: bytes32(0) - }), + address(0xdead), 0, 0, + bytes32(0), 1, + address(0), IResourceMetering.ResourceConfig({ maxResourceLimit: 1, elasticityMultiplier: 1, @@ -219,15 +212,12 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall( systemConfig.initialize, ( - ISystemConfig.Roles({ - owner: address(0xdead), - feeAdmin: address(0xdead), - unsafeBlockSigner: address(0), - batcherHash: bytes32(0) - }), + address(0xdead), 0, 0, + bytes32(0), 1, + address(0), IResourceMetering.ResourceConfig({ maxResourceLimit: 1, elasticityMultiplier: 1, @@ -270,6 +260,14 @@ contract Initializer_Test is Bridge_Initializer { ) }) ); + // L2CrossDomainMessenger + contracts.push( + InitializeableContract({ + name: "L2CrossDomainMessenger", + target: address(l2CrossDomainMessenger), + initCalldata: abi.encodeCall(l2CrossDomainMessenger.initialize, (l1CrossDomainMessenger)) + }) + ); // L1StandardBridgeImpl contracts.push( InitializeableContract({ @@ -290,6 +288,22 @@ contract Initializer_Test is Bridge_Initializer { ) }) ); + // L2StandardBridge + contracts.push( + InitializeableContract({ + name: "L2StandardBridge", + target: address(l2StandardBridge), + initCalldata: abi.encodeCall(l2StandardBridge.initialize, (l1StandardBridge)) + }) + ); + // L2StandardBridgeInterop + contracts.push( + InitializeableContract({ + name: "L2StandardBridgeInterop", + target: address(l2StandardBridge), + initCalldata: abi.encodeCall(l2StandardBridge.initialize, (l1StandardBridge)) + }) + ); // L1ERC721BridgeImpl contracts.push( InitializeableContract({ @@ -306,18 +320,26 @@ contract Initializer_Test is Bridge_Initializer { initCalldata: abi.encodeCall(l1ERC721Bridge.initialize, (l1CrossDomainMessenger, superchainConfig)) }) ); - // L1OptimismMintableERC20FactoryImpl + // L2ERC721Bridge + contracts.push( + InitializeableContract({ + name: "L2ERC721Bridge", + target: address(l2ERC721Bridge), + initCalldata: abi.encodeCall(l2ERC721Bridge.initialize, (payable(address(l1ERC721Bridge)))) + }) + ); + // OptimismMintableERC20FactoryImpl contracts.push( InitializeableContract({ - name: "L1OptimismMintableERC20Factory", + name: "OptimismMintableERC20Factory", target: deploy.mustGetAddress("OptimismMintableERC20Factory"), initCalldata: abi.encodeCall(l1OptimismMintableERC20Factory.initialize, (address(l1StandardBridge))) }) ); - // L1OptimismMintableERC20FactoryProxy + // OptimismMintableERC20FactoryProxy contracts.push( InitializeableContract({ - name: "L1OptimismMintableERC20FactoryProxy", + name: "OptimismMintableERC20FactoryProxy", target: address(l1OptimismMintableERC20Factory), initCalldata: abi.encodeCall(l1OptimismMintableERC20Factory.initialize, (address(l1StandardBridge))) }) @@ -465,7 +487,7 @@ contract Initializer_Test is Bridge_Initializer { // Then, attempt to re-initialize the contract. This should fail. (bool success, bytes memory returnData) = _contract.target.call(_contract.initCalldata); assertFalse(success); - assertErrorString(returnData); + assertEq(_extractErrorString(returnData), "Initializable: contract is already initialized"); } } @@ -487,12 +509,10 @@ contract Initializer_Test is Bridge_Initializer { real_ = bytes(nicknames[_name]).length > 0 ? nicknames[_name] : _name; } - /// @dev Asserts the expected revert from the returndata - function assertErrorString(bytes memory _returnData) internal pure returns (string memory error_) { - if (bytes4(_returnData) == InvalidInitialization.selector) { - // do nothing as this is the correct 4byte returndata - } else if (bytes4(_returnData) == 0x08c379a0) { - // The first 4 bytes of the return data should be the selector for `Error(string)`. If not, revert. + /// @dev Extracts the revert string from returndata encoded in the form of `Error(string)`. + function _extractErrorString(bytes memory _returnData) internal pure returns (string memory error_) { + // The first 4 bytes of the return data should be the selector for `Error(string)`. If not, revert. + if (bytes4(_returnData) == 0x08c379a0) { // Extract the error string from the returndata. The error string is located 68 bytes after // the pointer to `returnData`. // @@ -503,7 +523,6 @@ contract Initializer_Test is Bridge_Initializer { assembly { error_ := add(_returnData, 0x44) } - assertEq(error_, "Initializable: contract is already initialized"); } else { revert("Initializer_Test: Invalid returndata format. Expected `Error(string)`"); } From 21e1291ae707ce3252682839246df8921a43558e Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Thu, 7 Nov 2024 21:27:57 +0100 Subject: [PATCH 152/451] make custom process bash function (#12704) * make custom process bash function * rm Executables.sol, fix failing test * fixes * fixes... --- .../contracts-bedrock/scripts/Artifacts.s.sol | 6 +- .../contracts-bedrock/scripts/L2Genesis.s.sol | 6 +- .../scripts/autogen/SemverLock.s.sol | 26 ++- .../scripts/deploy/Deploy.s.sol | 20 +-- .../scripts/deploy/DeployConfig.s.sol | 17 +- .../scripts/deploy/Deployer.sol | 13 +- .../scripts/libraries/Executables.sol | 34 ---- .../scripts/libraries/ForgeArtifacts.sol | 166 ++++++------------ .../scripts/libraries/Process.sol | 19 ++ .../contracts-bedrock/test/L2/L2Genesis.t.sol | 99 +++++------ .../test/vendor/Initializable.t.sol | 16 +- 11 files changed, 158 insertions(+), 264 deletions(-) delete mode 100644 packages/contracts-bedrock/scripts/libraries/Executables.sol diff --git a/packages/contracts-bedrock/scripts/Artifacts.s.sol b/packages/contracts-bedrock/scripts/Artifacts.s.sol index 3ed021ea9dbb..87a87f61fb90 100644 --- a/packages/contracts-bedrock/scripts/Artifacts.s.sol +++ b/packages/contracts-bedrock/scripts/Artifacts.s.sol @@ -67,11 +67,7 @@ abstract contract Artifacts { /// as well as the JSON files that contain addresses in the `superchain-registry` /// repo. The JSON key is the name of the contract and the value is an address. function _loadAddresses(string memory _path) internal { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("jq -cr < ", _path); - string memory json = string(Process.run(commands)); + string memory json = Process.bash(string.concat("jq -cr < ", _path)); string[] memory keys = vm.parseJsonKeys(json, ""); for (uint256 i; i < keys.length; i++) { string memory key = keys[i]; diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 207c59732146..0f681185a551 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -642,11 +642,7 @@ contract L2Genesis is Deployer { /// @notice Sorts the allocs by address function sortJsonByKeys(string memory _path) internal { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("cat <<< $(jq -S '.' ", _path, ") > ", _path); - Process.run(commands); + Process.bash(string.concat("cat <<< $(jq -S '.' ", _path, ") > ", _path)); } /// @notice Funds the default dev accounts with ether diff --git a/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol b/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol index 1d5aeb963e3d..00b0f1855fb1 100644 --- a/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol +++ b/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol @@ -9,11 +9,8 @@ import { Process } from "scripts/libraries/Process.sol"; contract SemverLock is Script { function run() public { // First, find all contracts with a Semver inheritance. - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = "grep -rl '@custom:semver' src | jq -Rs 'split(\"\\n\") | map(select(length > 0))'"; - string memory rawFiles = string(Process.run(commands)); + string memory rawFiles = + Process.bash("grep -rl '@custom:semver' src | jq -Rs 'split(\"\\n\") | map(select(length > 0))'"); string[] memory files = vm.parseJsonStringArray(rawFiles, ""); writeSemverLock(files); @@ -30,22 +27,21 @@ contract SemverLock is Script { string memory fileContents = string(Process.run(commands)); // Grab the contract name - commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("echo \"", _files[i], "\"| sed -E \'s|src/.*/(.+)\\.sol|\\1|\'"); - string memory contractName = string(Process.run(commands)); + string memory contractName = + Process.bash(string.concat("echo \"", _files[i], "\"| sed -E 's|src/.*/(.+)\\.sol|\\1|'")); - commands[2] = "forge config --json | jq -r .out"; - string memory artifactsDir = string(Process.run(commands)); + string memory artifactsDir = Process.bash("forge config --json | jq -r .out"); // Handle the case where there are multiple artifacts for a contract. This happens // when the same contract is compiled with multiple compiler versions. string memory contractArtifactDir = string.concat(artifactsDir, "/", contractName, ".sol"); - commands[2] = string.concat( - "ls -1 --color=never ", contractArtifactDir, " | jq -R -s -c 'split(\"\n\") | map(select(length > 0))'" + string memory artifactFiles = Process.bash( + string.concat( + "ls -1 --color=never ", + contractArtifactDir, + " | jq -R -s -c 'split(\"\n\") | map(select(length > 0))'" + ) ); - string memory artifactFiles = string(Process.run(commands)); string[] memory files = stdJson.readStringArray(artifactFiles, ""); require(files.length > 0, string.concat("SemverLock: no artifacts found for ", contractName)); diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index d40c03987d53..33e1578ea8bb 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -995,17 +995,13 @@ contract Deploy is Deployer { function _loadDevnetStMipsAbsolutePrestate() internal returns (Claim mipsAbsolutePrestate_) { // Fetch the absolute prestate dump string memory filePath = string.concat(vm.projectRoot(), "/../../op-program/bin/prestate-proof.json"); - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("[[ -f ", filePath, " ]] && echo \"present\""); - if (Process.run(commands).length == 0) { + if (bytes(Process.bash(string.concat("[[ -f ", filePath, " ]] && echo \"present\""))).length == 0) { revert( "Deploy: cannon prestate dump not found, generate it with `make cannon-prestate` in the monorepo root" ); } - commands[2] = string.concat("cat ", filePath, " | jq -r .pre"); - mipsAbsolutePrestate_ = Claim.wrap(abi.decode(Process.run(commands), (bytes32))); + mipsAbsolutePrestate_ = + Claim.wrap(abi.decode(bytes(Process.bash(string.concat("cat ", filePath, " | jq -r .pre"))), (bytes32))); console.log( "[Cannon Dispute Game] Using devnet MIPS Absolute prestate: %s", vm.toString(Claim.unwrap(mipsAbsolutePrestate_)) @@ -1017,17 +1013,13 @@ contract Deploy is Deployer { function _loadDevnetMtMipsAbsolutePrestate() internal returns (Claim mipsAbsolutePrestate_) { // Fetch the absolute prestate dump string memory filePath = string.concat(vm.projectRoot(), "/../../op-program/bin/prestate-proof-mt.json"); - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("[[ -f ", filePath, " ]] && echo \"present\""); - if (Process.run(commands).length == 0) { + if (bytes(Process.bash(string.concat("[[ -f ", filePath, " ]] && echo \"present\""))).length == 0) { revert( "Deploy: MT-Cannon prestate dump not found, generate it with `make cannon-prestate-mt` in the monorepo root" ); } - commands[2] = string.concat("cat ", filePath, " | jq -r .pre"); - mipsAbsolutePrestate_ = Claim.wrap(abi.decode(Process.run(commands), (bytes32))); + mipsAbsolutePrestate_ = + Claim.wrap(abi.decode(bytes(Process.bash(string.concat("cat ", filePath, " | jq -r .pre"))), (bytes32))); console.log( "[MT-Cannon Dispute Game] Using devnet MIPS2 Absolute prestate: %s", vm.toString(Claim.unwrap(mipsAbsolutePrestate_)) diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index 69341dd3e874..be5f95ef39ca 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.15; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; import { stdJson } from "forge-std/StdJson.sol"; -import { Executables } from "scripts/libraries/Executables.sol"; import { Process } from "scripts/libraries/Process.sol"; import { Config, Fork, ForkUtils } from "scripts/libraries/Config.sol"; @@ -211,12 +210,9 @@ contract DeployConfig is Script { function l2OutputOracleStartingTimestamp() public returns (uint256) { if (_l2OutputOracleStartingTimestamp < 0) { bytes32 tag = l1StartingBlockTag(); - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat("cast block ", vm.toString(tag), " --json | ", Executables.jq, " .timestamp"); - bytes memory res = Process.run(cmd); - return stdJson.readUint(string(res), ""); + string memory cmd = string.concat("cast block ", vm.toString(tag), " --json | jq .timestamp"); + string memory res = Process.bash(cmd); + return stdJson.readUint(res, ""); } return uint256(_l2OutputOracleStartingTimestamp); } @@ -263,11 +259,8 @@ contract DeployConfig is Script { } function _getBlockByTag(string memory _tag) internal returns (bytes32) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat("cast block ", _tag, " --json | ", Executables.jq, " -r .hash"); - bytes memory res = Process.run(cmd); + string memory cmd = string.concat("cast block ", _tag, " --json | jq -r .hash"); + bytes memory res = bytes(Process.bash(cmd)); return abi.decode(res, (bytes32)); } diff --git a/packages/contracts-bedrock/scripts/deploy/Deployer.sol b/packages/contracts-bedrock/scripts/deploy/Deployer.sol index 36de8dd440fc..fa7454f5ee1d 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deployer.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deployer.sol @@ -5,8 +5,8 @@ import { Script } from "forge-std/Script.sol"; import { Artifacts } from "scripts/Artifacts.s.sol"; import { Config } from "scripts/libraries/Config.sol"; import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; -import { Executables } from "scripts/libraries/Executables.sol"; import { console } from "forge-std/console.sol"; +import { Process } from "scripts/libraries/Process.sol"; /// @title Deployer /// @author tynes @@ -19,11 +19,20 @@ abstract contract Deployer is Script, Artifacts { function setUp() public virtual override { Artifacts.setUp(); - console.log("Commit hash: %s", Executables.gitCommitHash()); + console.log("Commit hash: %s", gitCommitHash()); vm.etch(address(cfg), vm.getDeployedCode("DeployConfig.s.sol:DeployConfig")); vm.label(address(cfg), "DeployConfig"); vm.allowCheatcodes(address(cfg)); cfg.read(Config.deployConfigPath()); } + + /// @notice Returns the commit hash of HEAD. If no git repository is + /// found, it will return the contents of the .gitcommit file. Otherwise, + /// it will return an error. The .gitcommit file is used to store the + /// git commit of the contracts when they are packaged into docker images + /// in order to avoid the need to have a git repository in the image. + function gitCommitHash() internal returns (string memory) { + return Process.bash("cast abi-encode 'f(string)' $(git rev-parse HEAD || cat .gitcommit)"); + } } diff --git a/packages/contracts-bedrock/scripts/libraries/Executables.sol b/packages/contracts-bedrock/scripts/libraries/Executables.sol deleted file mode 100644 index 0dc91e32072d..000000000000 --- a/packages/contracts-bedrock/scripts/libraries/Executables.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { Vm } from "forge-std/Vm.sol"; -import { Process } from "scripts/libraries/Process.sol"; - -/// @notice The executables used in ffi commands. These are set here -/// to have a single source of truth in case absolute paths -/// need to be used. -library Executables { - /// @notice Foundry cheatcode VM. - Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); - string internal constant bash = "bash"; - string internal constant jq = "jq"; - string internal constant forge = "forge"; - string internal constant echo = "echo"; - string internal constant sed = "sed"; - string internal constant find = "find"; - string internal constant ls = "ls"; - string internal constant git = "git"; - - /// @notice Returns the commit hash of HEAD. If no git repository is - /// found, it will return the contents of the .gitcommit file. Otherwise, - /// it will return an error. The .gitcommit file is used to store the - /// git commit of the contracts when they are packaged into docker images - /// in order to avoid the need to have a git repository in the image. - function gitCommitHash() internal returns (string memory) { - string[] memory commands = new string[](3); - commands[0] = bash; - commands[1] = "-c"; - commands[2] = "cast abi-encode 'f(string)' $(git rev-parse HEAD || cat .gitcommit)"; - return abi.decode(Process.run(commands), (string)); - } -} diff --git a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol index 944206694d78..dba01345d05e 100644 --- a/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol +++ b/packages/contracts-bedrock/scripts/libraries/ForgeArtifacts.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import { Vm } from "forge-std/Vm.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { LibString } from "@solady/utils/LibString.sol"; -import { Executables } from "scripts/libraries/Executables.sol"; import { Process } from "scripts/libraries/Process.sol"; /// @notice Contains information about a storage slot. Mirrors the layout of the storage @@ -37,14 +36,7 @@ library ForgeArtifacts { /// @notice Removes the semantic versioning from a contract name. The semver will exist if the contract is compiled /// more than once with different versions of the compiler. function _stripSemver(string memory _name) internal returns (string memory out_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat( - Executables.echo, " ", _name, " | ", Executables.sed, " -E 's/[.][0-9]+\\.[0-9]+\\.[0-9]+//g'" - ); - bytes memory res = Process.run(cmd); - out_ = string(res); + out_ = Process.bash(string.concat("echo ", _name, " | sed -E 's/[.][0-9]+\\.[0-9]+\\.[0-9]+//g'")); } /// @notice Builds the fully qualified name of a contract. Assumes that the @@ -56,48 +48,33 @@ library ForgeArtifacts { /// @notice Returns the storage layout for a deployed contract. function getStorageLayout(string memory _name) internal returns (string memory layout_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat(Executables.jq, " -r '.storageLayout' < ", _getForgeArtifactPath(_name)); - bytes memory res = Process.run(cmd); - layout_ = string(res); + layout_ = Process.bash(string.concat("jq -r '.storageLayout' < ", _getForgeArtifactPath(_name))); } /// @notice Returns the abi from a the forge artifact function getAbi(string memory _name) internal returns (string memory abi_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat(Executables.jq, " -r '.abi' < ", _getForgeArtifactPath(_name)); - bytes memory res = Process.run(cmd); - abi_ = string(res); + abi_ = Process.bash(string.concat("jq -r '.abi' < ", _getForgeArtifactPath(_name))); } /// @notice Returns the methodIdentifiers from the forge artifact function getMethodIdentifiers(string memory _name) internal returns (string[] memory ids_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat(Executables.jq, " '.methodIdentifiers // {} | keys ' < ", _getForgeArtifactPath(_name)); - bytes memory res = Process.run(cmd, true); - ids_ = stdJson.readStringArray(string(res), ""); + string memory res = Process.bash({ + _command: string.concat("jq '.methodIdentifiers // {} | keys ' < ", _getForgeArtifactPath(_name)), + _allowEmpty: true + }); + ids_ = stdJson.readStringArray(res, ""); } /// @notice Returns the kind of contract (i.e. library, contract, or interface). /// @param _name The name of the contract to get the kind of. /// @return kind_ The kind of contract ("library", "contract", or "interface"). function getContractKind(string memory _name) internal returns (string memory kind_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat( - Executables.jq, - " -r '.ast.nodes[] | select(.nodeType == \"ContractDefinition\") | .contractKind' < ", - _getForgeArtifactPath(_name) + kind_ = Process.bash( + string.concat( + "jq -r '.ast.nodes[] | select(.nodeType == \"ContractDefinition\") | .contractKind' < ", + _getForgeArtifactPath(_name) + ) ); - bytes memory res = Process.run(cmd); - kind_ = string(res); } /// @notice Returns whether or not a contract is proxied. @@ -109,19 +86,14 @@ library ForgeArtifacts { // contract. We should consider determining whether a contract is proxied based on the // deployment script since it's the source of truth for that. Current deployment script // does not make this easy but an updated script should likely make this possible. - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat( - Executables.jq, - " -r '.rawMetadata' ", - _getForgeArtifactPath(_name), - " | ", - Executables.jq, - " -r '.output.devdoc' | jq -r 'has(\"custom:proxied\")'" + string memory res = Process.bash( + string.concat( + "jq -r '.rawMetadata' ", + _getForgeArtifactPath(_name), + " | jq -r '.output.devdoc' | jq -r 'has(\"custom:proxied\")'" + ) ); - bytes memory res = Process.run(cmd); - out_ = stdJson.readBool(string(res), ""); + out_ = stdJson.readBool(res, ""); } /// @notice Returns whether or not a contract is predeployed. @@ -130,27 +102,18 @@ library ForgeArtifacts { function isPredeployedContract(string memory _name) internal returns (bool out_) { // TODO: Similar to the above, using the `@custom:predeployed` tag is not reliable but // functional for now. Deployment script should make this easier to determine. - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat( - Executables.jq, - " -r '.rawMetadata' ", - _getForgeArtifactPath(_name), - " | ", - Executables.jq, - " -r '.output.devdoc' | jq -r 'has(\"custom:predeploy\")'" + string memory res = Process.bash( + string.concat( + "jq -r '.rawMetadata' ", + _getForgeArtifactPath(_name), + " | jq -r '.output.devdoc' | jq -r 'has(\"custom:predeploy\")'" + ) ); - bytes memory res = Process.run(cmd); - out_ = stdJson.readBool(string(res), ""); + out_ = stdJson.readBool(res, ""); } function _getForgeArtifactDirectory(string memory _name) internal returns (string memory dir_) { - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat(Executables.forge, " config --json | ", Executables.jq, " -r .out"); - bytes memory res = Process.run(cmd); + string memory res = Process.bash("forge config --json | jq -r .out"); string memory contractName = _stripSemver(_name); dir_ = string.concat(vm.projectRoot(), "/", string(res), "/", contractName, ".sol"); } @@ -164,19 +127,10 @@ library ForgeArtifacts { return path; } - string[] memory cmd = new string[](3); - cmd[0] = Executables.bash; - cmd[1] = "-c"; - cmd[2] = string.concat( - Executables.ls, - " -1 --color=never ", - directory, - " | ", - Executables.jq, - " -R -s -c 'split(\"\n\") | map(select(length > 0))'" + string memory res = Process.bash( + string.concat("ls -1 --color=never ", directory, " | jq -R -s -c 'split(\"\n\") | map(select(length > 0))'") ); - bytes memory res = Process.run(cmd); - string[] memory files = stdJson.readStringArray(string(res), ""); + string[] memory files = stdJson.readStringArray(res, ""); out_ = string.concat(directory, "/", files[0]); } @@ -198,23 +152,19 @@ library ForgeArtifacts { slotType = "t_bool"; } - string[] memory command = new string[](3); - command[0] = Executables.bash; - command[1] = "-c"; - command[2] = string.concat( - Executables.echo, - " '", - storageLayout, - "'", - " | ", - Executables.jq, - " '.storage[] | select(.label == \"", - slotName, - "\" and .type == \"", - slotType, - "\")'" + bytes memory rawSlot = vm.parseJson( + Process.bash( + string.concat( + "echo '", + storageLayout, + "' | jq '.storage[] | select(.label == \"", + slotName, + "\" and .type == \"", + slotType, + "\")'" + ) + ) ); - bytes memory rawSlot = vm.parseJson(string(Process.run(command))); slot_ = abi.decode(rawSlot, (StorageSlot)); } @@ -245,25 +195,19 @@ library ForgeArtifacts { } } - string[] memory command = new string[](3); - command[0] = Executables.bash; - command[1] = "-c"; - command[2] = string.concat( - Executables.find, - " ", - _path, - bytes(pathExcludesPat).length > 0 ? string.concat(" ! \\( ", pathExcludesPat, " \\)") : "", - " -type f ", - "-exec basename {} \\;", - " | ", - Executables.sed, - " 's/\\.[^.]*$//'", - " | ", - Executables.jq, - " -R -s 'split(\"\n\")[:-1]'" + contractNames_ = abi.decode( + vm.parseJson( + Process.bash( + string.concat( + "find ", + _path, + bytes(pathExcludesPat).length > 0 ? string.concat(" ! \\( ", pathExcludesPat, " \\)") : "", + " -type f -exec basename {} \\; | sed 's/\\.[^.]*$//' | jq -R -s 'split(\"\n\")[:-1]'" + ) + ) + ), + (string[]) ); - - contractNames_ = abi.decode(vm.parseJson(string(Process.run(command))), (string[])); } /// @notice Returns the function ABIs of all L1 contracts. diff --git a/packages/contracts-bedrock/scripts/libraries/Process.sol b/packages/contracts-bedrock/scripts/libraries/Process.sol index d2cf5c3af4aa..7a8c939f11f8 100644 --- a/packages/contracts-bedrock/scripts/libraries/Process.sol +++ b/packages/contracts-bedrock/scripts/libraries/Process.sol @@ -10,6 +10,25 @@ library Process { /// @notice Foundry cheatcode VM. Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + /// @notice Executes a bash command in a subprocess and returns its output as a string. Will revert if the command + /// returns no output. + /// @param _command The bash command to execute + function bash(string memory _command) internal returns (string memory stdout_) { + stdout_ = bash({ _command: _command, _allowEmpty: false }); + } + + /// @notice Executes a bash command in a subprocess and returns its output as a string. Will 'optionally' revert if + /// the command returns no output. + /// @param _command The bash command to execute + /// @param _allowEmpty Allow empty output. + function bash(string memory _command, bool _allowEmpty) internal returns (string memory stdout_) { + string[] memory command = new string[](3); + command[0] = "bash"; + command[1] = "-c"; + command[2] = _command; + stdout_ = string(run({ _command: command, _allowEmpty: _allowEmpty })); + } + /// @notice Run a command in a subprocess. Fails if no output is returned. /// @param _command Command to run. function run(string[] memory _command) internal returns (bytes memory stdout_) { diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 57b22e628eba..6d9884bd677a 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -22,32 +22,21 @@ contract L2GenesisTest is Test { /// @notice Creates a temp file and returns the path to it. function tmpfile() internal returns (string memory) { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = "mktemp"; - bytes memory result = Process.run(commands); - return string(result); + return Process.bash("mktemp"); } /// @notice Deletes a file at a given filesystem path. Does not force delete /// and does not recursively delete. function deleteFile(string memory path) internal { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("rm ", path); - Process.run({ _command: commands, _allowEmpty: true }); + Process.bash(string.concat("rm ", path), true); } /// @notice Returns the number of top level keys in a JSON object at a given /// file path. function getJSONKeyCount(string memory path) internal returns (uint256) { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat("jq 'keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'"); - return abi.decode(Process.run(commands), (uint256)); + bytes memory result = + bytes(Process.bash(string.concat("jq 'keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'"))); + return abi.decode(result, (uint256)); } /// @notice Helper function to run a function with a temporary dump file. @@ -59,43 +48,44 @@ contract L2GenesisTest is Test { /// @notice Helper function for reading the number of storage keys for a given account. function getStorageKeysCount(string memory _path, address _addr) internal returns (uint256) { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = - string.concat("jq -r '.[\"", vm.toLowercase(vm.toString(_addr)), "\"].storage | length' < ", _path); - return vm.parseUint(string(Process.run(commands))); + return vm.parseUint( + Process.bash( + string.concat("jq -r '.[\"", vm.toLowercase(vm.toString(_addr)), "\"].storage | length' < ", _path) + ) + ); } /// @notice Returns the number of accounts that contain particular code at a given path to a genesis file. function getCodeCount(string memory path, string memory name) internal returns (uint256) { bytes memory code = vm.getDeployedCode(name); - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat( - "jq -r 'map_values(select(.code == \"", - vm.toString(code), - "\")) | length' < ", - path, - " | xargs cast abi-encode 'f(uint256)'" + bytes memory result = bytes( + Process.bash( + string.concat( + "jq -r 'map_values(select(.code == \"", + vm.toString(code), + "\")) | length' < ", + path, + " | xargs cast abi-encode 'f(uint256)'" + ) + ) ); - return abi.decode(Process.run(commands), (uint256)); + return abi.decode(result, (uint256)); } /// @notice Returns the number of accounts that have a particular slot set. function getPredeployCountWithSlotSet(string memory path, bytes32 slot) internal returns (uint256) { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat( - "jq 'map_values(.storage | select(has(\"", - vm.toString(slot), - "\"))) | keys | length' < ", - path, - " | xargs cast abi-encode 'f(uint256)'" + bytes memory result = bytes( + Process.bash( + string.concat( + "jq 'map_values(.storage | select(has(\"", + vm.toString(slot), + "\"))) | keys | length' < ", + path, + " | xargs cast abi-encode 'f(uint256)'" + ) + ) ); - return abi.decode(Process.run(commands), (uint256)); + return abi.decode(result, (uint256)); } /// @notice Returns the number of accounts that have a particular slot set to a particular value. @@ -107,19 +97,20 @@ contract L2GenesisTest is Test { internal returns (uint256) { - string[] memory commands = new string[](3); - commands[0] = "bash"; - commands[1] = "-c"; - commands[2] = string.concat( - "jq 'map_values(.storage | select(.\"", - vm.toString(slot), - "\" == \"", - vm.toString(value), - "\")) | length' < ", - path, - " | xargs cast abi-encode 'f(uint256)'" + bytes memory result = bytes( + Process.bash( + string.concat( + "jq 'map_values(.storage | select(.\"", + vm.toString(slot), + "\" == \"", + vm.toString(value), + "\")) | length' < ", + path, + " | xargs cast abi-encode 'f(uint256)'" + ) + ) ); - return abi.decode(Process.run(commands), (uint256)); + return abi.decode(result, (uint256)); } /// @notice Tests the genesis predeploys setup using a temp file for the case where useInterop is false. diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 5c17987c471d..9d60ab71e7c8 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -5,7 +5,6 @@ pragma solidity 0.8.15; import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; // Scripts -import { Executables } from "scripts/libraries/Executables.sol"; import { ForgeArtifacts } from "scripts/libraries/ForgeArtifacts.sol"; import { Process } from "scripts/libraries/Process.sol"; @@ -432,21 +431,14 @@ contract Initializer_Test is Bridge_Initializer { } // Construct the query for the initialize function in the contract's ABI. - string[] memory command = new string[](3); - command[0] = Executables.bash; - command[1] = "-c"; - command[2] = string.concat( - Executables.echo, - " '", + string memory cmd = string.concat( + "echo '", ForgeArtifacts.getAbi(contractName), - "'", - " | ", - Executables.jq, - " '.[] | select(.name == \"initialize\" and .type == \"function\")'" + "' | jq '.[] | select(.name == \"initialize\" and .type == \"function\")'" ); // If the contract does not have an `initialize()` function, skip it. - if (Process.run(command).length == 0) { + if (bytes(Process.bash(cmd)).length == 0) { continue; } From cdc52a14126ac93882a31acbe090a93c490f3716 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Thu, 7 Nov 2024 21:29:31 +0100 Subject: [PATCH 153/451] add createX runtime bytecode hash check (#12776) --- packages/contracts-bedrock/test/L2/Preinstalls.t.sol | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol index ad14e5ea32fb..be0d01181e81 100644 --- a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol +++ b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol @@ -118,4 +118,15 @@ contract PreinstallsTest is CommonTest { function test_preinstall_createX_succeeds() external view { assertPreinstall(Preinstalls.CreateX, Preinstalls.CreateXCode); } + + function test_createX_runtimeBytecodeHash() external view { + bytes memory createXRuntimeBytecode = Preinstalls.CreateX.code; + bytes32 createXRuntimeBytecodeHash = keccak256(createXRuntimeBytecode); + + assertEq( + createXRuntimeBytecodeHash, + 0xbd8a7ea8cfca7b4e5f5041d7d4b17bc317c5ce42cfbc42066a00cf26b43eb53f, + "CreateX runtime bytecode hash mismatch" + ); + } } From ed6a629026432c4c46bb8194d36c9d5cf6668474 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Thu, 7 Nov 2024 21:31:17 +0100 Subject: [PATCH 154/451] move slither related files to test/slither (#12788) --- packages/contracts-bedrock/{ => test/slither}/slither.config.json | 0 packages/contracts-bedrock/{ => test/slither}/slither.db.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/contracts-bedrock/{ => test/slither}/slither.config.json (100%) rename packages/contracts-bedrock/{ => test/slither}/slither.db.json (100%) diff --git a/packages/contracts-bedrock/slither.config.json b/packages/contracts-bedrock/test/slither/slither.config.json similarity index 100% rename from packages/contracts-bedrock/slither.config.json rename to packages/contracts-bedrock/test/slither/slither.config.json diff --git a/packages/contracts-bedrock/slither.db.json b/packages/contracts-bedrock/test/slither/slither.db.json similarity index 100% rename from packages/contracts-bedrock/slither.db.json rename to packages/contracts-bedrock/test/slither/slither.db.json From 04d79720d895780b60c953efad147ae0d1fafc6b Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Thu, 7 Nov 2024 21:32:42 +0100 Subject: [PATCH 155/451] apply exclude in test name checks and add it to ci and just check (#12805) * apply exclude in test name checks and add it to ci and just check * rename names/ to test-names/ * add and use no build test names check command * apply test name checks to half of excludes test files * apply test name checks to last half of excludes test files --- .circleci/config.yml | 2 + packages/contracts-bedrock/justfile | 10 +++- .../checks/{names => test-names}/main.go | 6 ++ .../test/L1/DataAvailabilityChallenge.t.sol | 60 +++++++++++-------- .../test/L1/DelayedVetoable.t.sol | 2 +- .../test/L1/L1CrossDomainMessenger.t.sol | 8 +-- .../test/L1/OptimismPortal.t.sol | 12 ++-- .../test/L1/OptimismPortal2.t.sol | 20 +++---- .../test/L1/OptimismPortalInterop.t.sol | 6 +- .../test/L1/SystemConfig.t.sol | 10 ++-- .../test/L2/CrossL2Inbox.t.sol | 4 +- .../test/L2/L1BlockInterop.t.sol | 18 +++--- .../test/L2/L2CrossDomainMessenger.t.sol | 8 +-- .../contracts-bedrock/test/L2/L2Genesis.t.sol | 6 +- .../test/L2/OptimismSuperchainERC20.t.sol | 4 +- .../L2/OptimismSuperchainERC20Beacon.t.sol | 2 +- .../test/L2/Predeploys.t.sol | 2 +- .../test/L2/Preinstalls.t.sol | 4 +- .../test/L2/SuperchainWETH.t.sol | 8 +-- .../contracts-bedrock/test/cannon/MIPS.t.sol | 8 +-- .../contracts-bedrock/test/cannon/MIPS2.t.sol | 14 ++--- .../test/cannon/MIPS64Memory.t.sol | 6 +- .../test/cannon/PreimageOracle.t.sol | 2 +- .../test/libraries/Blueprint.t.sol | 6 +- .../test/libraries/TransientContext.t.sol | 4 +- .../test/opcm/DeployAuthSystem.t.sol | 6 +- .../test/opcm/DeployImplementations.t.sol | 8 +-- .../test/opcm/DeployOPChain.t.sol | 8 +-- .../test/opcm/DeploySuperchain.t.sol | 6 +- .../test/safe/DeployOwnership.t.sol | 6 +- .../test/safe/LivenessGuard.t.sol | 6 +- .../test/safe/LivenessModule.t.sol | 2 +- .../test/setup/DeployVariations.t.sol | 4 +- .../universal/OptimismMintableERC20.t.sol | 4 +- .../test/universal/Specs.t.sol | 4 +- 35 files changed, 155 insertions(+), 131 deletions(-) rename packages/contracts-bedrock/scripts/checks/{names => test-names}/main.go (96%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 030c15ae6367..9fbed7bde44d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -706,6 +706,8 @@ jobs: command: size-check - run-contracts-check: command: unused-imports-check-no-build + - run-contracts-check: + command: lint-forge-tests-check-no-build contracts-bedrock-validate-spacers: docker: diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 41cfd24bb2d6..9be9bc4e09ea 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -165,9 +165,12 @@ semver-diff-check: build semver-diff-check-no-build semgrep-test-validity-check: forge fmt ../../.semgrep/tests/sol-rules.t.sol --check +# Checks that forge test names are correctly formatted. Does not build contracts. +lint-forge-tests-check-no-build: + go run ./scripts/checks/test-names + # Checks that forge test names are correctly formatted. -lint-forge-tests-check: - go run ./scripts/checks/names +lint-forge-tests-check: build lint-forge-tests-check-no-build # Checks that contracts are properly linted. lint-check: @@ -216,7 +219,8 @@ check: semver-diff-check-no-build \ validate-deploy-configs \ validate-spacers-no-build \ - interfaces-check-no-build + interfaces-check-no-build \ + lint-forge-tests-check-no-build ######################################################## # DEV TOOLS # diff --git a/packages/contracts-bedrock/scripts/checks/names/main.go b/packages/contracts-bedrock/scripts/checks/test-names/main.go similarity index 96% rename from packages/contracts-bedrock/scripts/checks/names/main.go rename to packages/contracts-bedrock/scripts/checks/test-names/main.go index 1a0be03f33f2..86550f211cba 100644 --- a/packages/contracts-bedrock/scripts/checks/names/main.go +++ b/packages/contracts-bedrock/scripts/checks/test-names/main.go @@ -18,6 +18,8 @@ type CheckInfo struct { error string } +var excludes = map[string]bool{} + var checks = []CheckInfo{ { error: "test name parts should be in camelCase", @@ -97,6 +99,10 @@ func main() { return nil } + if excludes[strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))] { + return nil + } + data, err := os.ReadFile(path) if err != nil { return err diff --git a/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol b/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol index dd14b349c68a..ddbacec67c3e 100644 --- a/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol +++ b/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol @@ -17,20 +17,20 @@ contract DataAvailabilityChallengeTest is CommonTest { super.setUp(); } - function testDeposit() public { + function test_deposit_succeeds() public { assertEq(dataAvailabilityChallenge.balances(address(this)), 0); dataAvailabilityChallenge.deposit{ value: 1000 }(); assertEq(dataAvailabilityChallenge.balances(address(this)), 1000); } - function testReceive() public { + function test_receive_succeeds() public { assertEq(dataAvailabilityChallenge.balances(address(this)), 0); (bool success,) = payable(address(dataAvailabilityChallenge)).call{ value: 1000 }(""); assertTrue(success); assertEq(dataAvailabilityChallenge.balances(address(this)), 1000); } - function testWithdraw(address sender, uint256 amount) public { + function test_withdraw_succeeds(address sender, uint256 amount) public { assumePayable(sender); assumeNotPrecompile(sender); // EntryPoint will revert if using amount > type(uint112).max. @@ -52,7 +52,13 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(sender.balance, amount); } - function testChallengeSuccess(address challenger, uint256 challengedBlockNumber, bytes calldata preImage) public { + function test_challenge_succeeds( + address challenger, + uint256 challengedBlockNumber, + bytes calldata preImage + ) + public + { bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); // Assume the challenger is not the 0 address @@ -99,7 +105,13 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(dataAvailabilityChallenge.balances(challenger), 0); } - function testChallengeDeposit(address challenger, uint256 challengedBlockNumber, bytes memory preImage) public { + function test_challenge_deposit_succeeds( + address challenger, + uint256 challengedBlockNumber, + bytes memory preImage + ) + public + { bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); // Assume the challenger is not the 0 address @@ -142,7 +154,7 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(dataAvailabilityChallenge.balances(challenger), 0); } - function testChallengeFailBondTooLow() public { + function test_challenge_bondTooLow_reverts() public { uint256 requiredBond = dataAvailabilityChallenge.bondSize(); uint256 actualBond = requiredBond - 1; dataAvailabilityChallenge.deposit{ value: actualBond }(); @@ -153,7 +165,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.challenge(0, computeCommitmentKeccak256("some hash")); } - function testChallengeFailChallengeExists() public { + function test_challenge_challengeExists_reverts() public { // Move to a block after the hash to challenge vm.roll(2); @@ -176,7 +188,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.challenge(0, computeCommitmentKeccak256("some other hash")); } - function testChallengeFailBeforeChallengeWindow() public { + function test_challenge_beforeChallengeWindow_reverts() public { uint256 challengedBlockNumber = 1; bytes memory challengedCommitment = computeCommitmentKeccak256("some hash"); @@ -189,7 +201,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.challenge(challengedBlockNumber, challengedCommitment); } - function testChallengeFailAfterChallengeWindow() public { + function test_challenge_afterChallengeWindow_reverts() public { uint256 challengedBlockNumber = 1; bytes memory challengedCommitment = computeCommitmentKeccak256("some hash"); @@ -202,7 +214,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.challenge(challengedBlockNumber, challengedCommitment); } - function testResolveSuccess( + function test_resolve_succeeds( address challenger, address resolver, bytes memory preImage, @@ -283,7 +295,7 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(address(0).balance - zeroAddressBalanceBeforeResolve, burned, "burned bond"); } - function testResolveFailNonExistentChallenge() public { + function test_resolve_nonExistentChallenge_reverts() public { bytes memory preImage = "some preimage"; uint256 challengedBlockNumber = 1; @@ -295,7 +307,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.resolve(challengedBlockNumber, computeCommitmentKeccak256(preImage), preImage); } - function testResolveFailResolved() public { + function test_resolve_resolved_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -315,7 +327,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.resolve(challengedBlockNumber, challengedCommitment, preImage); } - function testResolveFailExpired() public { + function test_resolve_expired_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -335,7 +347,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.resolve(challengedBlockNumber, challengedCommitment, preImage); } - function testResolveFailAfterResolveWindow() public { + function test_resolve_afterResolveWindow_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -355,7 +367,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.resolve(challengedBlockNumber, challengedCommitment, preImage); } - function testUnlockBondSuccess(bytes memory preImage, uint256 challengedBlockNumber) public { + function test_unlockBond_succeeds(bytes memory preImage, uint256 challengedBlockNumber) public { // Assume the block number is not close to the max uint256 value vm.assume( challengedBlockNumber @@ -400,7 +412,7 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(dataAvailabilityChallenge.balances(address(this)), balanceAfterUnlock); } - function testUnlockBondFailNonExistentChallenge() public { + function test_unlockBond_nonExistentChallenge_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -413,7 +425,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.unlockBond(challengedBlockNumber, challengedCommitment); } - function testUnlockBondFailResolvedChallenge() public { + function test_unlockBond_resolvedChallenge_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -433,7 +445,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.unlockBond(challengedBlockNumber, challengedCommitment); } - function testUnlockBondExpiredChallengeTwice() public { + function test_unlockBond_expiredChallengeTwice_fails() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -458,7 +470,7 @@ contract DataAvailabilityChallengeTest is CommonTest { assertEq(dataAvailabilityChallenge.balances(address(this)), balanceAfterUnlock); } - function testUnlockFailResolveWindowNotClosed() public { + function test_unlockBond_resolveWindowNotClosed_reverts() public { bytes memory preImage = "some preimage"; bytes memory challengedCommitment = computeCommitmentKeccak256(preImage); uint256 challengedBlockNumber = 1; @@ -477,7 +489,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.unlockBond(challengedBlockNumber, challengedCommitment); } - function testSetBondSize() public { + function test_setBondSize_succeeds() public { uint256 requiredBond = dataAvailabilityChallenge.bondSize(); uint256 actualBond = requiredBond - 1; dataAvailabilityChallenge.deposit{ value: actualBond }(); @@ -497,14 +509,14 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.challenge(0, challengedCommitment); } - function testSetResolverRefundPercentage(uint256 resolverRefundPercentage) public { + function test_setResolverRefundPercentage_succeeds(uint256 resolverRefundPercentage) public { resolverRefundPercentage = bound(resolverRefundPercentage, 0, 100); vm.prank(dataAvailabilityChallenge.owner()); dataAvailabilityChallenge.setResolverRefundPercentage(resolverRefundPercentage); assertEq(dataAvailabilityChallenge.resolverRefundPercentage(), resolverRefundPercentage); } - function testSetResolverRefundPercentageFail() public { + function test_setResolverRefundPercentage_invalidResolverRefundPercentage_reverts() public { address owner = dataAvailabilityChallenge.owner(); vm.expectRevert( abi.encodeWithSelector(IDataAvailabilityChallenge.InvalidResolverRefundPercentage.selector, 101) @@ -513,7 +525,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.setResolverRefundPercentage(101); } - function testSetBondSizeFailOnlyOwner(address notOwner, uint256 newBondSize) public { + function test_setBondSize_onlyOwner_reverts(address notOwner, uint256 newBondSize) public { vm.assume(notOwner != dataAvailabilityChallenge.owner()); // Expect setting the bond size to fail because the sender is not the owner @@ -522,7 +534,7 @@ contract DataAvailabilityChallengeTest is CommonTest { dataAvailabilityChallenge.setBondSize(newBondSize); } - function testValidateCommitment() public { + function test_validateCommitment_succeeds() public { // Should not revert given a valid commitment bytes memory validCommitment = abi.encodePacked(CommitmentType.Keccak256, keccak256("test")); dataAvailabilityChallenge.validateCommitment(validCommitment); diff --git a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol b/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol index 6eade75e8ffa..a0dd2d8c138e 100644 --- a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol +++ b/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol @@ -61,7 +61,7 @@ contract DelayedVetoable_Init is Test { contract DelayedVetoable_Getters_Test is DelayedVetoable_Init { /// @dev The getters return the expected values when called by the zero address. - function test_getters() external { + function test_getters_succeeds() external { vm.startPrank(address(0)); assertEq(delayedVetoable.initiator(), initiator); assertEq(delayedVetoable.vetoer(), vetoer); diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 08a976aa0ec2..9ef537e435b3 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -623,7 +623,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that sendMessage succeeds with a custom gas token when the call value is zero. - function test_sendMessage_customGasToken_noValue_succeeds() external { + function test_sendMessage_customGasTokenButNoValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) @@ -671,7 +671,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that the sendMessage reverts when call value is non-zero with custom gas token. - function test_sendMessage_customGasToken_withValue_reverts() external { + function test_sendMessage_customGasTokenWithValue_reverts() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) @@ -682,7 +682,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that the relayMessage succeeds with a custom gas token when the call value is zero. - function test_relayMessage_customGasToken_noValue_succeeds() external { + function test_relayMessage_customGasTokenAndNoValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) @@ -722,7 +722,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that the relayMessage reverts when call value is non-zero with custom gas token. /// The L2CrossDomainMessenger contract cannot `sendMessage` with value when using a custom gas token. - function test_relayMessage_customGasToken_withValue_reverts() external virtual { + function test_relayMessage_customGasTokenWithValue_reverts() external virtual { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2)) diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol index 028fc7587043..70b1c6d4ed94 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol @@ -773,7 +773,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_ether_succeeds() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashEther_succeeds() external { uint256 bobBalanceBefore = address(bob).balance; vm.expectEmit(true, true, true, true); @@ -789,7 +789,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_targetToken_reverts() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashNonEtherTargetToken_reverts() external { vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), @@ -1483,7 +1483,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_succeeds() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashNonEther_succeeds() external { // Mint the token to the contract and approve the token for the portal token.mint(address(this), _defaultTx.value); token.approve(address(optimismPortal), _defaultTx.value); @@ -1562,7 +1562,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. /// forge-config: ciheavy.fuzz.runs = 8192 - function testFuzz_depositTransaction_customGasToken_noValue_senderIsOrigin_succeeds( + function testFuzz_depositTransaction_customGasTokenWithNoValueAndSenderIsOrigin_succeeds( address _to, uint256 _value, uint64 _gasLimit, @@ -1586,7 +1586,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. /// forge-config: ciheavy.fuzz.runs = 8192 - function testFuzz_depositTransaction_customGasToken_noValue_senderNotOrigin_succeeds( + function testFuzz_depositTransaction_customGasTokenWithNoValueAndSenderNotOrigin_succeeds( address _to, uint256 _value, uint64 _gasLimit, @@ -1609,7 +1609,7 @@ contract OptimismPortalWithMockERC20_Test is OptimismPortal_FinalizeWithdrawal_T } /// @dev Tests that `depositTransaction` fails when a custom gas token is used and msg.value is non-zero. - function test_depositTransaction_customGasToken_withValue_reverts() external { + function test_depositTransaction_customGasTokenWithValue_reverts() external { // Mock the gas paying token to be the ERC20 token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index 8cc56937bd33..d252609e5eea 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -583,7 +583,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { /// @dev Tests that `proveWithdrawalTransaction` reverts when the withdrawal has already been proven, and the new /// game has the `CHALLENGER_WINS` status. - function test_proveWithdrawalTransaction_replayProve_differentGameChallengerWins_reverts() external { + function test_proveWithdrawalTransaction_replayProveDifferentGameChallengerWins_reverts() external { vm.expectEmit(address(optimismPortal2)); emit WithdrawalProven(_withdrawalHash, alice, bob); vm.expectEmit(address(optimismPortal2)); @@ -632,7 +632,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against has been /// blacklisted. - function test_proveWithdrawalTransaction_replayProveBlacklisted_suceeds() external { + function test_proveWithdrawalTransaction_replayProveBlacklisted_succeeds() external { vm.expectEmit(true, true, true, true); emit WithdrawalProven(_withdrawalHash, alice, bob); vm.expectEmit(true, true, true, true); @@ -670,7 +670,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against has resolved /// against the favor of the root claim. - function test_proveWithdrawalTransaction_replayProveBadProposal_suceeds() external { + function test_proveWithdrawalTransaction_replayProveBadProposal_succeeds() external { vm.expectEmit(true, true, true, true); emit WithdrawalProven(_withdrawalHash, alice, bob); vm.expectEmit(true, true, true, true); @@ -704,7 +704,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against is no longer /// of the respected game type. - function test_proveWithdrawalTransaction_replayRespectedGameTypeChanged_suceeds() external { + function test_proveWithdrawalTransaction_replayRespectedGameTypeChanged_succeeds() external { // Prove the withdrawal against a game with the current respected game type. vm.expectEmit(true, true, true, true); emit WithdrawalProven(_withdrawalHash, alice, bob); @@ -803,7 +803,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_ether_succeeds() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashEther_succeeds() external { uint256 bobBalanceBefore = address(bob).balance; vm.expectEmit(address(optimismPortal2)); @@ -891,7 +891,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_targetToken_reverts() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashNonEtherTargetToken_reverts() external { vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), @@ -1730,7 +1730,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. - function test_finalizeWithdrawalTransaction_provenWithdrawalHash_nonEther_succeeds() external { + function test_finalizeWithdrawalTransaction_provenWithdrawalHashWithNonEther_succeeds() external { // Mint the token to the contract and approve the token for the portal token.mint(address(this), _defaultTx.value); token.approve(address(optimismPortal2), _defaultTx.value); @@ -1818,7 +1818,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. /// forge-config: ciheavy.fuzz.runs = 8192 - function testFuzz_depositTransaction_customGasToken_noValue_senderIsOrigin_succeeds( + function testFuzz_depositTransaction_customGasTokenWithNoValueAndSenderIsOrigin_succeeds( address _to, uint256 _value, uint64 _gasLimit, @@ -1842,7 +1842,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal /// @dev Tests that `depositTransaction` succeeds when a custom gas token is used but the msg.value is zero. /// forge-config: ciheavy.fuzz.runs = 8192 - function testFuzz_depositTransaction_customGasToken_noValue_senderNotOrigin_succeeds( + function testFuzz_depositTransaction_customGasTokenWithNoValueAndSenderNotOrigin_succeeds( address _to, uint256 _value, uint64 _gasLimit, @@ -1865,7 +1865,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal } /// @dev Tests that `depositTransaction` fails when a custom gas token is used and msg.value is non-zero. - function test_depositTransaction_customGasToken_withValue_reverts() external { + function test_depositTransaction_customGasTokenWithValue_reverts() external { // Mock the gas paying token to be the ERC20 token vm.mockCall( address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol index bc9a980276aa..bd6d3b5d7de9 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol @@ -43,7 +43,7 @@ contract OptimismPortalInterop_Test is CommonTest { } /// @dev Tests that setting the gas paying token config as not the system config reverts. - function testFuzz_setConfig_gasPayingToken_notSystemConfig_reverts(bytes calldata _value) public { + function testFuzz_setConfig_gasPayingTokenButNotSystemConfig_reverts(bytes calldata _value) public { vm.expectRevert(Unauthorized.selector); _optimismPortalInterop().setConfig(ConfigType.SET_GAS_PAYING_TOKEN, _value); } @@ -66,7 +66,7 @@ contract OptimismPortalInterop_Test is CommonTest { } /// @dev Tests that setting the add dependency config as not the system config reverts. - function testFuzz_setConfig_addDependency_notSystemConfig_reverts(bytes calldata _value) public { + function testFuzz_setConfig_addDependencyButNotSystemConfig_reverts(bytes calldata _value) public { vm.expectRevert(Unauthorized.selector); _optimismPortalInterop().setConfig(ConfigType.ADD_DEPENDENCY, _value); } @@ -89,7 +89,7 @@ contract OptimismPortalInterop_Test is CommonTest { } /// @dev Tests that setting the remove dependency config as not the system config reverts. - function testFuzz_setConfig_removeDependency_notSystemConfig_reverts(bytes calldata _value) public { + function testFuzz_setConfig_removeDependencyButNotSystemConfig_reverts(bytes calldata _value) public { vm.expectRevert(Unauthorized.selector); _optimismPortalInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, _value); } diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index dff0852fb40d..a6311c02b1e4 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -396,7 +396,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { } /// @dev Tests that initialization sets the correct values and getters work when token address passed is 0. - function test_initialize_customGasToken_zeroTokenAddress_succeeds() external { + function test_initialize_customGasTokenWithZeroTokenAddress_succeeds() external { cleanStorageAndInit(address(0)); (address addr, uint8 decimals) = systemConfig.gasPayingToken(); @@ -408,7 +408,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { } /// @dev Tests that initialization sets the correct values and getters work when token address is Constants.ETHER - function test_initialize_customGasToken_etherTokenAddress_succeeds() external { + function test_initialize_customGasTokenWithEtherTokenAddress_succeeds() external { cleanStorageAndInit(Constants.ETHER); (address addr, uint8 decimals) = systemConfig.gasPayingToken(); @@ -420,7 +420,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { } /// @dev Tests that initialization fails if decimals are not 18. - function test_initialize_customGasToken_wrongDecimals_fails() external { + function test_initialize_customGasTokenWrongDecimals_fails() external { vm.mockCall(address(token), abi.encodeCall(token.decimals, ()), abi.encode(8)); vm.expectRevert("SystemConfig: bad decimals of gas paying token"); @@ -428,7 +428,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { } /// @dev Tests that initialization fails if name is too long. - function test_initialize_customGasToken_nameTooLong_fails() external { + function test_initialize_customGasTokenNameTooLong_fails() external { string memory name = new string(32); name = string.concat(name, "a"); @@ -439,7 +439,7 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init { } /// @dev Tests that initialization fails if symbol is too long. - function test_initialize_customGasToken_symbolTooLong_fails() external { + function test_initialize_customGasTokenSymbolTooLong_fails() external { string memory symbol = new string(33); symbol = string.concat(symbol, "a"); diff --git a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol index 9e79906efeec..25b6e711c1ec 100644 --- a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol @@ -328,7 +328,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `executeMessage` function reverts when called with an identifier with a timestamp earlier /// than INTEROP_START timestamp - function testFuzz_executeMessage_invalidTimestamp_interopStart_reverts( + function testFuzz_executeMessage_invalidTimestampInteropStart_reverts( Identifier memory _id, address _target, bytes calldata _message, @@ -513,7 +513,7 @@ contract CrossL2InboxTest is Test { /// @dev Tests that the `validateMessage` function reverts when called with an identifier with a timestamp earlier /// than INTEROP_START timestamp - function testFuzz_validateMessage_invalidTimestamp_interopStart_reverts( + function testFuzz_validateMessage_invalidTimestampInteropStart_reverts( Identifier memory _id, bytes32 _messageHash ) diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol index 6f0ef2188b8c..c8779342c3bf 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol @@ -104,7 +104,7 @@ contract L1BlockInteropTest is CommonTest { } /// @dev Tests that setting the gas paying token config as not the depositor reverts. - function testFuzz_setConfig_gasPayingToken_notDepositor_reverts( + function testFuzz_setConfig_gasPayingTokenButNotDepositor_reverts( address _token, uint8 _decimals, bytes32 _name, @@ -132,13 +132,13 @@ contract L1BlockInteropTest is CommonTest { } /// @dev Tests that adding a dependency reverts if it's the chain's chain id - function test_setConfig_addDependency_chainChainId_reverts() public prankDepositor { + function test_setConfig_addDependencyButChainChainId_reverts() public prankDepositor { vm.expectRevert(AlreadyDependency.selector); _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(block.chainid)); } /// @dev Tests that adding a dependency already in the set reverts - function test_setConfig_addDependency_alreadyDependency_reverts(uint256 _chainId) public prankDepositor { + function test_setConfig_addDependencyButAlreadyDependency_reverts(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); @@ -148,13 +148,13 @@ contract L1BlockInteropTest is CommonTest { } /// @dev Tests that setting the add dependency config as not the depositor reverts. - function testFuzz_setConfig_addDependency_notDepositor_reverts(uint256 _chainId) public { + function testFuzz_setConfig_addDependencyButNotDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)); } /// @dev Tests that setting the add dependency config when the dependency set size is too large reverts. - function test_setConfig_addDependency_dependencySetSizeTooLarge_reverts() public prankDepositor { + function test_setConfig_addDependencyButDependencySetSizeTooLarge_reverts() public prankDepositor { for (uint256 i = 0; i < type(uint8).max; i++) { _l1BlockInterop().setConfig(ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(i)); } @@ -179,19 +179,19 @@ contract L1BlockInteropTest is CommonTest { } /// @dev Tests that setting the remove dependency config as not the depositor reverts. - function testFuzz_setConfig_removeDependency_notDepositor_reverts(uint256 _chainId) public { + function testFuzz_setConfig_removeDependencyButNotDepositor_reverts(uint256 _chainId) public { vm.expectRevert(NotDepositor.selector); _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)); } /// @dev Tests that setting the remove dependency config for the chain's chain ID reverts. - function test_setConfig_removeDependency_chainChainId_reverts() public prankDepositor { + function test_setConfig_removeDependencyButChainChainId_reverts() public prankDepositor { vm.expectRevert(CantRemovedDependency.selector); _l1BlockInterop().setConfig(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(block.chainid)); } /// @dev Tests that setting the remove dependency config for a chain ID that is not in the dependency set reverts. - function testFuzz_setConfig_removeDependency_notDependency_reverts(uint256 _chainId) public prankDepositor { + function testFuzz_setConfig_removeDependencyButNotDependency_reverts(uint256 _chainId) public prankDepositor { vm.assume(_chainId != block.chainid); vm.expectRevert(NotDependency.selector); @@ -284,7 +284,7 @@ contract L1BlockInteropSetL1BlockValuesInterop_Test is L1BlockInteropTest { contract L1BlockDepositsComplete_Test is L1BlockInteropTest { // @dev Tests that `depositsComplete` reverts if the caller is not the depositor. - function test_deposits_is_depositor_reverts(address _caller) external { + function test_depositsComplete_notDepositor_reverts(address _caller) external { vm.assume(_caller != _l1BlockInterop().DEPOSITOR_ACCOUNT()); vm.expectRevert(NotDepositor.selector); _l1BlockInterop().depositsComplete(); diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index 1dc5749d6d54..a2cd9382ca84 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -226,7 +226,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that sendMessage succeeds with a custom gas token when the call value is zero. - function test_sendMessage_customGasToken_noValue_succeeds() external { + function test_sendMessage_customGasTokenButNoValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); @@ -266,7 +266,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that the sendMessage reverts when call value is non-zero with custom gas token. - function test_sendMessage_customGasToken_withValue_reverts() external { + function test_sendMessage_customGasTokenWithValue_reverts() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); @@ -275,7 +275,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { } /// @dev Tests that the relayMessage succeeds with a custom gas token when the call value is zero. - function test_relayMessage_customGasToken_noValue_succeeds() external { + function test_relayMessage_customGasTokenAndNoValue_succeeds() external { // Mock the gasPayingToken function to return a custom gas token vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); @@ -311,7 +311,7 @@ contract L2CrossDomainMessenger_Test is Bridge_Initializer { /// @dev Tests that the relayMessage reverts when call value is non-zero with custom gas token. /// The L1CrossDomainMessenger `sendMessage` function cannot send value with a custom gas token. - function test_relayMessage_customGasToken_withValue_reverts() external virtual { + function test_relayMessage_customGasTokenWithValue_reverts() external virtual { // Mock the gasPayingToken function to return a custom gas token vm.mockCall(address(l1Block), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(2))); vm.expectRevert("CrossDomainMessenger: value must be zero unless message is from a system address"); diff --git a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol index 6d9884bd677a..2398ac332df5 100644 --- a/packages/contracts-bedrock/test/L2/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/L2/L2Genesis.t.sol @@ -114,14 +114,14 @@ contract L2GenesisTest is Test { } /// @notice Tests the genesis predeploys setup using a temp file for the case where useInterop is false. - function test_genesis_predeploys_notUsingInterop() external { + function test_genesisPredeploys_notUsingInterop_works() external { string memory path = tmpfile(); _test_genesis_predeploys(path, false); deleteFile(path); } /// @notice Tests the genesis predeploys setup using a temp file for the case where useInterop is true. - function test_genesis_predeploys_usingInterop() external { + function test_genesisPredeploys_usingInterop_works() external { string memory path = tmpfile(); _test_genesis_predeploys(path, true); deleteFile(path); @@ -154,7 +154,7 @@ contract L2GenesisTest is Test { } /// @notice Tests the number of accounts in the genesis setup - function test_allocs_size() external { + function test_allocs_size_works() external { withTempDump(_test_allocs_size); } diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol index e1dc1286d2bd..d5b999a922e5 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol @@ -102,7 +102,7 @@ contract OptimismSuperchainERC20Test is Test { } /// @notice Tests the `initialize` function reverts when the contract is already initialized. - function testFuzz_initializer_reverts( + function testFuzz_initializer_invalidInitialization_reverts( address _remoteToken, string memory _name, string memory _symbol, @@ -256,7 +256,7 @@ contract OptimismSuperchainERC20Test is Test { /// @notice Tests that the `supportsInterface` function returns false for any other interface than the /// `ISuperchainERC20` one. - function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { + function testFuzz_supportInterface_returnFalse_works(bytes4 _interfaceId) public view { vm.assume(_interfaceId != type(IERC165).interfaceId); vm.assume(_interfaceId != type(IERC20).interfaceId); vm.assume(_interfaceId != type(IERC7802).interfaceId); diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol index 68217127e0cb..4de8b06b8ee0 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol @@ -18,7 +18,7 @@ contract OptimismSuperchainERC20BeaconTest is Bridge_Initializer { } /// @notice Test that calling the implementation function returns the correct implementation address. - function test_implementation_is_correct() public view { + function test_implementation_isCorrect_works() public view { IBeacon beacon = IBeacon(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON); assertEq(beacon.implementation(), Predeploys.OPTIMISM_SUPERCHAIN_ERC20); } diff --git a/packages/contracts-bedrock/test/L2/Predeploys.t.sol b/packages/contracts-bedrock/test/L2/Predeploys.t.sol index 27e3d75b2fdc..6cc36c45c9e1 100644 --- a/packages/contracts-bedrock/test/L2/Predeploys.t.sol +++ b/packages/contracts-bedrock/test/L2/Predeploys.t.sol @@ -34,7 +34,7 @@ contract PredeploysBaseTest is CommonTest { || _addr == Predeploys.GOVERNANCE_TOKEN; } - function test_predeployToCodeNamespace() external pure { + function test_predeployToCodeNamespace_works() external pure { assertEq( address(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000), Predeploys.predeployToCodeNamespace(Predeploys.LEGACY_MESSAGE_PASSER) diff --git a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol index be0d01181e81..a9202346be6a 100644 --- a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol +++ b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol @@ -9,7 +9,7 @@ import { IEIP712 } from "src/universal/interfaces/IEIP712.sol"; /// @title PreinstallsTest contract PreinstallsTest is CommonTest { /// @dev The domain separator commits to the chainid of the chain - function test_preinstall_permit2_domain_separator() external view { + function test_preinstall_permit2DomainSeparator_works() external view { bytes32 domainSeparator = IEIP712(Preinstalls.Permit2).DOMAIN_SEPARATOR(); bytes32 typeHash = keccak256(abi.encodePacked("EIP712Domain(string name,uint256 chainId,address verifyingContract)")); @@ -23,7 +23,7 @@ contract PreinstallsTest is CommonTest { // Warning the Permit2 domain separator as cached in the DeployPermit2.sol bytecode is incorrect. } - function test_permit2_templating() external pure { + function test_permit2_templating_works() external pure { bytes memory customCode = Preinstalls.getPermit2Code(1234); assertNotEq(customCode.length, 0, "must have code"); assertEq(uint256(bytes32(Bytes.slice(customCode, 6945, 32))), uint256(1234), "expecting custom chain ID"); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index 4d4109b6885a..d0744ecb48ef 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -322,7 +322,7 @@ contract SuperchainWETH_Test is CommonTest { } /// @notice Test that the internal mint function reverts to protect against accidentally changing the visibility. - function testFuzz_calling_internal_mint_function_reverts(address _caller, address _to, uint256 _amount) public { + function testFuzz_calling_internalMintFunction_reverts(address _caller, address _to, uint256 _amount) public { // Arrange bytes memory _calldata = abi.encodeWithSignature("_mint(address,uint256)", _to, _amount); // nosemgrep: // sol-style-use-abi-encodecall @@ -337,7 +337,7 @@ contract SuperchainWETH_Test is CommonTest { } /// @notice Test that the mint function reverts to protect against accidentally changing the visibility. - function testFuzz_calling_mint_function_reverts(address _caller, address _to, uint256 _amount) public { + function testFuzz_calling_mintFunction_reverts(address _caller, address _to, uint256 _amount) public { // Arrange bytes memory _calldata = abi.encodeWithSignature("mint(address,uint256)", _to, _amount); // nosemgrep: // sol-style-use-abi-encodecall @@ -352,7 +352,7 @@ contract SuperchainWETH_Test is CommonTest { } /// @notice Test that the internal burn function reverts to protect against accidentally changing the visibility. - function testFuzz_calling_internal_burn_function_reverts(address _caller, address _from, uint256 _amount) public { + function testFuzz_calling_internalBurnFunction_reverts(address _caller, address _from, uint256 _amount) public { // Arrange bytes memory _calldata = abi.encodeWithSignature("_burn(address,uint256)", _from, _amount); // nosemgrep: // sol-style-use-abi-encodecall @@ -367,7 +367,7 @@ contract SuperchainWETH_Test is CommonTest { } /// @notice Test that the burn function reverts to protect against accidentally changing the visibility. - function testFuzz_calling_burn_function_reverts(address _caller, address _from, uint256 _amount) public { + function testFuzz_calling_burnFuunction_reverts(address _caller, address _from, uint256 _amount) public { // Arrange bytes memory _calldata = abi.encodeWithSignature("burn(address,uint256)", _from, _amount); // nosemgrep: // sol-style-use-abi-encodecall diff --git a/packages/contracts-bedrock/test/cannon/MIPS.t.sol b/packages/contracts-bedrock/test/cannon/MIPS.t.sol index e4221dfcf66b..62100f31cfbe 100644 --- a/packages/contracts-bedrock/test/cannon/MIPS.t.sol +++ b/packages/contracts-bedrock/test/cannon/MIPS.t.sol @@ -1509,7 +1509,7 @@ contract MIPS_Test is CommonTest { assertEq(postState, outputState(expect), "unexpected post state"); } - function test_mmap_succeeds_justWithinMemLimit() external { + function test_mmap_justWithinMemLimit_succeeds() external { uint32 insn = 0x0000000c; // syscall (bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn); @@ -1538,7 +1538,7 @@ contract MIPS_Test is CommonTest { assertEq(postState, outputState(expect), "unexpected post state"); } - function test_mmap_fails() external { + function test_step_mmap_fails() external { uint32 insn = 0x0000000c; // syscall (bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn); @@ -1692,7 +1692,7 @@ contract MIPS_Test is CommonTest { mips.step(encodedState, proof, 0); } - function test_invalid_root_fails() external { + function test_step_invalidRoot_fails() external { uint32 insn = 0x0000000c; // syscall (IMIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0); state.registers[2] = 4246; // exit_group syscall @@ -1706,7 +1706,7 @@ contract MIPS_Test is CommonTest { mips.step(encodeState(state), proof, 0); } - function test_invalid_root_different_leaf_fails() external { + function test_step_invalidRootDifferentLeaf_fails() external { uint32 insn = 0x0000000c; // syscall // Initialize the state, though for the proof, use valid proofs for the instruction diff --git a/packages/contracts-bedrock/test/cannon/MIPS2.t.sol b/packages/contracts-bedrock/test/cannon/MIPS2.t.sol index 0c2ad04362bb..2cc0519ea5bf 100644 --- a/packages/contracts-bedrock/test/cannon/MIPS2.t.sol +++ b/packages/contracts-bedrock/test/cannon/MIPS2.t.sol @@ -159,7 +159,7 @@ contract MIPS2_Test is CommonTest { /// This is useful to more easily debug non-forge tests. /// For example, in cannon/mipsevm/evm_test.go step input can be pulled here: /// https://github.com/ethereum-optimism/optimism/blob/1f64dd6db5561f3bb76ed1d1ffdaff0cde9b7c4b/cannon/mipsevm/evm_test.go#L80-L80 - function test_mips2_step_debug_succeeds() external { + function test_step_debug_succeeds() external { bytes memory input = hex"e14ced3200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000acab5a39c6f974b22302e96dcdef1815483eaf580639bb1ee7ac98267afac2bf1ac041d3ff12045b73c86e4ff95ff662a5eee82abdf44a2d0b75fb180daf48a79e3143a81fa7c3d90b000000000000000000000078fc2ffac2fd940100000000000080c8ffffffff006504aeffb6e08baf3f85da5476a9160fa8f9f188a722fdd29268b0cbaf596736ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb500000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c6000000000000ffffffff000000000000000000000000f1f85ff4f1f85ff8506d442dbb3938f83eb60825a7ecbff2000010185e1a31f600050f0000000064a7c3d90be5acea102ad7bda149e0bfd0e7111c77d98b335645e665389becadf140ef999cc64fbd7f04799e85c97dadc5cca510bd5b3d97166d1aec28829f3dd43d8cf1f9358e4103b16d09d466e2c7c048ea3ba1aef3141e700270581aa0b75b50e34fc926bb2d83ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb500000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618db8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; (bool success, bytes memory retVal) = address(mips).call(input); @@ -246,7 +246,7 @@ contract MIPS2_Test is CommonTest { } } - function test_invalidThreadWitness_reverts() public { + function test_step_invalidThreadWitness_reverts() public { IMIPS2.State memory state; IMIPS2.ThreadState memory thread; bytes memory memProof; @@ -743,12 +743,12 @@ contract MIPS2_Test is CommonTest { } /// @dev Test asserting that an clock_gettime monotonic syscall reverts on an invalid memory proof - function test_syscallClockGettimeMonotonicInvalidProof_reverts() public { + function test_step_syscallClockGettimeMonotonicInvalidProof_reverts() public { _test_syscallClockGettimeInvalidProof_reverts(sys.CLOCK_GETTIME_MONOTONIC_FLAG); } /// @dev Test asserting that an clock_gettime realtime syscall reverts on an invalid memory proof - function test_syscallClockGettimeRealtimeInvalidProof_reverts() public { + function test_step_syscallClockGettimeRealtimeInvalidProof_reverts() public { _test_syscallClockGettimeInvalidProof_reverts(sys.CLOCK_GETTIME_REALTIME_FLAG); } @@ -1336,7 +1336,7 @@ contract MIPS2_Test is CommonTest { assertEq(postState, outputState(expect), "unexpected post state"); } - function test_mmap_succeeds_simple() external { + function test_mmap_simple_succeeds() external { uint32 insn = 0x0000000c; // syscall (IMIPS2.State memory state, IMIPS2.ThreadState memory thread, bytes memory memProof) = constructMIPSState(0, insn, 0x4, 0); @@ -1368,7 +1368,7 @@ contract MIPS2_Test is CommonTest { assertEq(postState, outputState(expect), "unexpected post state"); } - function test_mmap_succeeds_justWithinMemLimit() external { + function test_mmap_justWithinMemLimit_succeeds() external { uint32 insn = 0x0000000c; // syscall (IMIPS2.State memory state, IMIPS2.ThreadState memory thread, bytes memory memProof) = constructMIPSState(0, insn, 0x4, 0); @@ -1400,7 +1400,7 @@ contract MIPS2_Test is CommonTest { assertEq(postState, outputState(expect), "unexpected post state"); } - function test_mmap_fails() external { + function test_step_mmap_fails() external { uint32 insn = 0x0000000c; // syscall (IMIPS2.State memory state, IMIPS2.ThreadState memory thread, bytes memory memProof) = constructMIPSState(0, insn, 0x4, 0); diff --git a/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol index a3a1160c7d48..9ba0f0bcf5f5 100644 --- a/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol +++ b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol @@ -49,7 +49,7 @@ contract MIPS64Memory_Test is CommonTest { } /// @dev Static unit test asserting that reads revert when a misaligned memory address is provided - function test_readInvalidAddress_reverts() external { + function test_readMem_readInvalidAddress_reverts() external { uint64 addr = 0x100; uint64 word = 0x11_22_33_44_55_66_77_88; bytes32 root; @@ -60,7 +60,7 @@ contract MIPS64Memory_Test is CommonTest { } /// @dev Static unit test asserting that reads revert when an invalid proof is provided - function test_readInvalidProof_reverts() external { + function test_readMem_readInvalidProof_reverts() external { uint64 addr = 0x100; uint64 word = 0x11_22_33_44_55_66_77_88; bytes32 root; @@ -170,7 +170,7 @@ contract MIPS64Memory_Test is CommonTest { } /// @dev Static unit test asserting that writes revert when a misaligned memory address is provided - function test_writeMemInvalidAddress_reverts() external { + function test_writeMem_writeMemInvalidAddress_reverts() external { bytes memory zeroProof; (, zeroProof) = ffi.getCannonMemory64Proof(0x100, 0); vm.expectRevert(InvalidAddress.selector); diff --git a/packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol b/packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol index d43d52784ba5..1c4d41728fcc 100644 --- a/packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol +++ b/packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol @@ -391,7 +391,7 @@ contract PreimageOracle_LargePreimageProposals_Test is Test { } /// @notice Gas snapshot for `addLeaves` - function test_addLeaves_gasSnapshot() public { + function test_addLeaves_gasSnapshot_benchmark() public { // Allocate the preimage data. bytes memory data = new bytes(136 * 500); for (uint256 i; i < data.length; i++) { diff --git a/packages/contracts-bedrock/test/libraries/Blueprint.t.sol b/packages/contracts-bedrock/test/libraries/Blueprint.t.sol index c94616a88e4f..3e2a96a01912 100644 --- a/packages/contracts-bedrock/test/libraries/Blueprint.t.sol +++ b/packages/contracts-bedrock/test/libraries/Blueprint.t.sol @@ -56,7 +56,7 @@ contract Blueprint_Test is Test { // --- We start with the test cases from ERC-5202 --- // An example (and trivial!) blueprint contract with no data section, whose initcode is just the STOP instruction. - function test_ERC5202_trivialBlueprint_succeeds() public view { + function test_trivialBlueprint_erc5202_succeeds() public view { bytes memory bytecode = hex"FE710000"; Blueprint.Preamble memory preamble = blueprint.parseBlueprintPreamble(bytecode); @@ -67,7 +67,7 @@ contract Blueprint_Test is Test { // An example blueprint contract whose initcode is the trivial STOP instruction and whose data // section contains the byte 0xFF repeated seven times. - function test_ERC5202_blueprintWithDataSection_succeeds() public view { + function test_blueprintWithDataSection_erc5202_succeeds() public view { // Here, 0xFE71 is the magic header, 0x01 means version 0 + 1 length bit, 0x07 encodes the // length in bytes of the data section. These are followed by the data section, and then the // initcode. For illustration, this code with delimiters would be: @@ -82,7 +82,7 @@ contract Blueprint_Test is Test { // An example blueprint whose initcode is the trivial STOP instruction and whose data section // contains the byte 0xFF repeated 256 times. - function test_ERC5202_blueprintWithLargeDataSection_succeeds() public view { + function test_blueprintWithLargeDataSection_erc5205_succeeds() public view { // Delimited, this would be 0xFE71|02|0100|FF...FF|00 bytes memory bytecode = hex"FE71020100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00"; diff --git a/packages/contracts-bedrock/test/libraries/TransientContext.t.sol b/packages/contracts-bedrock/test/libraries/TransientContext.t.sol index a7b414ad3882..20e8434b9c16 100644 --- a/packages/contracts-bedrock/test/libraries/TransientContext.t.sol +++ b/packages/contracts-bedrock/test/libraries/TransientContext.t.sol @@ -88,7 +88,7 @@ contract TransientContextTest is Test { /// @param _slot Slot to test. /// @param _value1 Value to write to slot at call depth 0. /// @param _value2 Value to write to slot at call depth 1. - function testFuzz_setGet_twice_sameDepth_succeeds(bytes32 _slot, uint256 _value1, uint256 _value2) public { + function testFuzz_setGet_twiceSameDepth_succeeds(bytes32 _slot, uint256 _value1, uint256 _value2) public { assertEq(TransientContext.callDepth(), 0); testFuzz_set_succeeds(_slot, _value1); assertEq(TransientContext.get(_slot), _value1); @@ -102,7 +102,7 @@ contract TransientContextTest is Test { /// @param _slot Slot to test. /// @param _value1 Value to write to slot at call depth 0. /// @param _value2 Value to write to slot at call depth 1. - function testFuzz_setGet_twice_differentDepth_succeeds(bytes32 _slot, uint256 _value1, uint256 _value2) public { + function testFuzz_setGet_twiceDifferentDepth_succeeds(bytes32 _slot, uint256 _value1, uint256 _value2) public { assertEq(TransientContext.callDepth(), 0); testFuzz_set_succeeds(_slot, _value1); assertEq(TransientContext.get(_slot), _value1); diff --git a/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol b/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol index 58912409a09c..7126ce25efaf 100644 --- a/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployAuthSystem.t.sol @@ -30,7 +30,7 @@ contract DeployAuthSystemInput_Test is Test { } } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { vm.expectRevert("DeployAuthSystemInput: threshold not set"); dasi.threshold(); @@ -38,7 +38,7 @@ contract DeployAuthSystemInput_Test is Test { dasi.owners(); } - function test_setters_ownerAlreadySet_revert() public { + function test_setters_ownerAlreadySet_reverts() public { dasi.set(dasi.owners.selector, owners); vm.expectRevert("DeployAuthSystemInput: owners already set"); @@ -135,7 +135,7 @@ contract DeployAuthSystem_Test is Test { daso.checkOutput(); } - function test_run_NullInput_reverts() public { + function test_run_nullInput_reverts() public { dasi.set(dasi.owners.selector, defaultOwners); dasi.set(dasi.threshold.selector, defaultThreshold); diff --git a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol index d01ef6a387b2..0282ea0b3d90 100644 --- a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol @@ -44,7 +44,7 @@ contract DeployImplementationsInput_Test is Test { dii = new DeployImplementationsInput(); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { vm.expectRevert("DeployImplementationsInput: not set"); dii.withdrawalDelaySeconds(); @@ -157,7 +157,7 @@ contract DeployImplementationsOutput_Test is Test { assertEq(address(disputeGameFactoryImpl), address(dio.disputeGameFactoryImpl()), "950"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { bytes memory expectedErr = "DeployUtils: zero address"; vm.expectRevert(expectedErr); @@ -335,7 +335,7 @@ contract DeployImplementations_Test is Test { assertEq(srDisputeGameFactoryImpl, address(dio.disputeGameFactoryImpl())); } - function test_deployAtNonExistentRelease_reverts() public { + function test_deploy_atNonExistentRelease_reverts() public { string memory unknownRelease = "op-contracts/v0.0.0"; dii.set(dii.release.selector, unknownRelease); @@ -390,7 +390,7 @@ contract DeployImplementations_Test is Test { deployImplementations.deployDisputeGameFactoryImpl(dii, dio); } - function test_noContractExistsAtRelease_reverts() public { + function test_deploy_noContractExistsAtRelease_reverts() public { string memory unknownRelease = "op-contracts/v1.3.0"; dii.set(dii.release.selector, unknownRelease); bytes memory expectedErr = diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 54e0e456def3..5249ded41cc9 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -78,7 +78,7 @@ contract DeployOPChainInput_Test is Test { assertEq(true, doi.allowCustomDisputeParameters(), "1200"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { bytes memory expectedErr = "DeployOPChainInput: not set"; vm.expectRevert(expectedErr); @@ -182,7 +182,7 @@ contract DeployOPChainOutput_Test is Test { // "1600"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { bytes memory expectedErr = "DeployUtils: zero address"; vm.expectRevert(expectedErr); @@ -427,7 +427,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { return keccak256(abi.encode(_seed, _i)); } - function testFuzz_run_memory_succeed(bytes32 _seed) public { + function testFuzz_run_memory_succeeds(bytes32 _seed) public { opChainProxyAdminOwner = address(uint160(uint256(hash(_seed, 0)))); systemConfigOwner = address(uint160(uint256(hash(_seed, 1)))); batcher = address(uint160(uint256(hash(_seed, 2)))); @@ -541,7 +541,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { deployOPChain.run(doi, doo); } - function test_customDisputeGame_customEnabled_doesNotRevert() public { + function test_customDisputeGame_customEnabled_succeeds() public { setDOI(); doi.set(doi.allowCustomDisputeParameters.selector, true); doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth + 1); diff --git a/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol b/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol index 15e24f470f1c..924957cc1800 100644 --- a/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeploySuperchain.t.sol @@ -24,7 +24,7 @@ contract DeploySuperchainInput_Test is Test { dsi = new DeploySuperchainInput(); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { vm.expectRevert("DeploySuperchainInput: superchainProxyAdminOwner not set"); dsi.superchainProxyAdminOwner(); @@ -83,7 +83,7 @@ contract DeploySuperchainOutput_Test is Test { assertEq(address(protocolVersionsProxy), address(dso.protocolVersionsProxy()), "500"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { vm.expectRevert("DeployUtils: zero address"); dso.superchainConfigImpl(); @@ -194,7 +194,7 @@ contract DeploySuperchain_Test is Test { dso.checkOutput(dsi); } - function test_run_NullInput_reverts() public { + function test_run_nullInput_reverts() public { // Set default values for all inputs. dsi.set(dsi.superchainProxyAdminOwner.selector, defaultProxyAdminOwner); dsi.set(dsi.protocolVersionsOwner.selector, defaultProtocolVersionsOwner); diff --git a/packages/contracts-bedrock/test/safe/DeployOwnership.t.sol b/packages/contracts-bedrock/test/safe/DeployOwnership.t.sol index 973b888ddf68..835b05b80f6b 100644 --- a/packages/contracts-bedrock/test/safe/DeployOwnership.t.sol +++ b/packages/contracts-bedrock/test/safe/DeployOwnership.t.sol @@ -42,7 +42,7 @@ contract DeployOwnershipTest is Test, DeployOwnership { /// @dev Test the example Foundation Safe configurations, against the expected configuration, and /// check that they both have the same configuration. - function test_exampleFoundationSafes() public { + function test_exampleFoundationSafes_configuration_succeeds() public { Safe upgradeSafe = Safe(payable(mustGetAddress("FoundationUpgradeSafe"))); Safe operationsSafe = Safe(payable(mustGetAddress("FoundationOperationsSafe"))); SafeConfig memory exampleFoundationConfig = _getExampleFoundationConfig(); @@ -57,7 +57,7 @@ contract DeployOwnershipTest is Test, DeployOwnership { } /// @dev Test the example Security Council Safe configuration. - function test_exampleSecurityCouncilSafe() public { + function test_exampleSecurityCouncilSafe_configuration_succeeds() public { Safe securityCouncilSafe = Safe(payable(mustGetAddress("SecurityCouncilSafe"))); SecurityCouncilConfig memory exampleSecurityCouncilConfig = _getExampleCouncilConfig(); @@ -96,7 +96,7 @@ contract DeployOwnershipTest is Test, DeployOwnership { } /// @dev Test the example Guardian Safe configuration. - function test_exampleGuardianSafe() public view { + function test_exampleGuardianSafe_configuration_succeeds() public view { Safe guardianSafe = Safe(payable(mustGetAddress("GuardianSafe"))); address[] memory owners = new address[](1); owners[0] = mustGetAddress("SecurityCouncilSafe"); diff --git a/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol b/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol index 68ddddec5b37..a7b67b415b22 100644 --- a/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol +++ b/packages/contracts-bedrock/test/safe/LivenessGuard.t.sol @@ -68,7 +68,7 @@ contract LivenessGuard_Getters_Test is LivenessGuard_TestInit { contract LivenessGuard_CheckTx_TestFails is LivenessGuard_TestInit { /// @dev Tests that the checkTransaction function reverts if the caller is not the Safe - function test_checkTransaction_callerIsNotSafe_revert() external { + function test_checkTransaction_callerIsNotSafe_reverts() external { vm.expectRevert("LivenessGuard: only Safe can call this function"); livenessGuard.checkTransaction({ _to: address(0), @@ -123,7 +123,7 @@ contract LivenessGuard_CheckTx_Test is LivenessGuard_TestInit { contract LivenessGuard_CheckAfterExecution_TestFails is LivenessGuard_TestInit { /// @dev Tests that the checkAfterExecution function reverts if the caller is not the Safe - function test_checkAfterExecution_callerIsNotSafe_revert() external { + function test_checkAfterExecution_callerIsNotSafe_reverts() external { vm.expectRevert("LivenessGuard: only Safe can call this function"); livenessGuard.checkAfterExecution(bytes32(0), false); } @@ -229,7 +229,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness /// @dev Tests that the guard correctly manages the lastLive mapping when owners are added, removed, or swapped /// forge-config: ciheavy.fuzz.runs = 8192 - function testFuzz_OwnerManagement_works( + function testFuzz_ownerManagement_works( uint256 initialOwners, uint256 threshold, OwnerChange[] memory changes diff --git a/packages/contracts-bedrock/test/safe/LivenessModule.t.sol b/packages/contracts-bedrock/test/safe/LivenessModule.t.sol index e101f59ca5a4..5a6a30950227 100644 --- a/packages/contracts-bedrock/test/safe/LivenessModule.t.sol +++ b/packages/contracts-bedrock/test/safe/LivenessModule.t.sol @@ -488,7 +488,7 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { } /// @dev Tests if removing owners works correctly for various safe configurations and numbeers of live owners - function testFuzz_removeOwners( + function testFuzz_removeOwners_works( uint256 _numOwners, uint256 _minOwners, uint256 _numLiveOwners, diff --git a/packages/contracts-bedrock/test/setup/DeployVariations.t.sol b/packages/contracts-bedrock/test/setup/DeployVariations.t.sol index 348d3d8e5237..31f687f0fdb8 100644 --- a/packages/contracts-bedrock/test/setup/DeployVariations.t.sol +++ b/packages/contracts-bedrock/test/setup/DeployVariations.t.sol @@ -24,7 +24,7 @@ contract DeployVariations_Test is CommonTest { /// forge-config: ciheavy.fuzz.runs = 512 /// @dev It should be possible to enable Fault Proofs with any mix of CGT and Alt-DA. - function testFuzz_enableFaultProofs(bool _enableCGT, bool _enableAltDa) public virtual { + function testFuzz_enableFaultProofs_succeeds(bool _enableCGT, bool _enableAltDa) public virtual { enableAddOns(_enableCGT, _enableAltDa); super.setUp(); @@ -32,7 +32,7 @@ contract DeployVariations_Test is CommonTest { /// forge-config: ciheavy.fuzz.runs = 512 /// @dev It should be possible to enable Fault Proofs and Interop with any mix of CGT and Alt-DA. - function test_enableInteropAndFaultProofs(bool _enableCGT, bool _enableAltDa) public virtual { + function test_enableInteropAndFaultProofs_succeeds(bool _enableCGT, bool _enableAltDa) public virtual { enableAddOns(_enableCGT, _enableAltDa); super.enableInterop(); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol index 19b156e2f6e6..18e080372745 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol @@ -47,11 +47,11 @@ contract OptimismMintableERC20_Test is Bridge_Initializer { assertEq(L2Token.balanceOf(alice), 100); } - function test_allowance_permit2_max() external view { + function test_allowance_permit2Max_works() external view { assertEq(L2Token.allowance(alice, L2Token.PERMIT2()), type(uint256).max); } - function test_permit2_transferFrom() external { + function test_permit2_transferFrom_succeeds() external { vm.prank(address(l2StandardBridge)); L2Token.mint(alice, 100); diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 9c22e178a5f4..5150b4d725dd 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -952,7 +952,7 @@ contract Specification_Test is CommonTest { } /// @notice Ensures that there's an auth spec for every L1 contract function. - function testContractAuth() public { + function test_contractAuth_works() public { string[] memory pathExcludes = new string[](6); pathExcludes[0] = "src/dispute/interfaces/*"; pathExcludes[1] = "src/dispute/lib/*"; @@ -1002,7 +1002,7 @@ contract Specification_Test is CommonTest { } /// @notice Ensures that the DeputyGuardian is authorized to take all Guardian actions. - function testDeputyGuardianAuth() public view { + function test_deputyGuardianAuth_works() public view { assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, specsByRole[Role.GUARDIAN].length); assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, 5); From 6ea4e40b2133315174f15fbc80f071ce3791f8a8 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Thu, 7 Nov 2024 21:33:49 +0100 Subject: [PATCH 156/451] merge bridge initializer into common test (#12795) * merge bridge initializer into common test * gas snapshot * remove duplicate import --- packages/contracts-bedrock/.gas-snapshot | 4 +- .../test/L1/L1CrossDomainMessenger.t.sol | 6 +- .../test/L1/L1ERC721Bridge.t.sol | 8 +-- .../test/L1/L1StandardBridge.t.sol | 42 +++++------ .../test/L2/BaseFeeVault.t.sol | 4 +- .../test/L2/CrossDomainOwnable2.t.sol | 4 +- .../test/L2/CrossDomainOwnable3.t.sol | 4 +- .../test/L2/L1FeeVault.t.sol | 4 +- .../test/L2/L2CrossDomainMessenger.t.sol | 4 +- .../test/L2/L2ERC721Bridge.t.sol | 4 +- .../test/L2/L2StandardBridge.t.sol | 12 ++-- .../test/L2/L2StandardBridgeInterop.t.sol | 4 +- .../L2/OptimismSuperchainERC20Beacon.t.sol | 4 +- .../L2/OptimismSuperchainERC20Factory.t.sol | 4 +- .../test/L2/SuperchainTokenBridge.t.sol | 4 +- .../invariants/CrossDomainMessenger.t.sol | 5 +- .../test/setup/Bridge_Initializer.sol | 69 ------------------- .../test/setup/CommonTest.sol | 62 ++++++++++++++++- .../test/universal/BenchmarkTest.t.sol | 7 +- .../test/universal/CrossDomainMessenger.t.sol | 6 +- .../universal/OptimismMintableERC20.t.sol | 4 +- .../OptimismMintableERC20Factory.t.sol | 4 +- .../universal/OptimismMintableERC721.t.sol | 4 +- .../OptimismMintableERC721Factory.t.sol | 5 +- .../test/vendor/Initializable.t.sol | 7 +- 25 files changed, 136 insertions(+), 149 deletions(-) delete mode 100644 packages/contracts-bedrock/test/setup/Bridge_Initializer.sol diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/.gas-snapshot index 1b1a01905ba5..2ab3157a714e 100644 --- a/packages/contracts-bedrock/.gas-snapshot +++ b/packages/contracts-bedrock/.gas-snapshot @@ -5,7 +5,7 @@ GasBenchMark_L1BlockInterop_SetValuesInterop_Warm:test_setL1BlockValuesInterop_b GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (gas: 158531) GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7597) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369280) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967420) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967465) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564398) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076613) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467098) @@ -14,4 +14,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68422) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68986) -GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155610) \ No newline at end of file +GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155565) \ No newline at end of file diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 9ef537e435b3..23c1365e9156 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; // Libraries @@ -17,7 +17,7 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; -contract L1CrossDomainMessenger_Test is Bridge_Initializer { +contract L1CrossDomainMessenger_Test is CommonTest { /// @dev The receiver address address recipient = address(0xabbaacdc); @@ -742,7 +742,7 @@ contract L1CrossDomainMessenger_Test is Bridge_Initializer { /// @dev A regression test against a reentrancy vulnerability in the CrossDomainMessenger contract, which /// was possible by intercepting and sandwhiching a signed Safe Transaction to upgrade it. -contract L1CrossDomainMessenger_ReinitReentryTest is Bridge_Initializer { +contract L1CrossDomainMessenger_ReinitReentryTest is CommonTest { bool attacked; // Common values used across functions diff --git a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol index 055ba45d0e1b..3949bf30f4eb 100644 --- a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; @@ -25,7 +25,7 @@ contract TestERC721 is ERC721 { } } -contract L1ERC721Bridge_Test is Bridge_Initializer { +contract L1ERC721Bridge_Test is CommonTest { TestERC721 internal localToken; TestERC721 internal remoteToken; uint256 internal constant tokenId = 1; @@ -315,7 +315,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer { } } -contract L1ERC721Bridge_Pause_Test is Bridge_Initializer { +contract L1ERC721Bridge_Pause_Test is CommonTest { /// @dev Verifies that the `paused` accessor returns the same value as the `paused` function of the /// `superchainConfig`. function test_paused_succeeds() external view { @@ -343,7 +343,7 @@ contract L1ERC721Bridge_Pause_Test is Bridge_Initializer { } } -contract L1ERC721Bridge_Pause_TestFail is Bridge_Initializer { +contract L1ERC721Bridge_Pause_TestFail is CommonTest { /// @dev Sets up the test by pausing the bridge, giving ether to the bridge and mocking /// the calls to the xDomainMessageSender so that it returns the correct value. function setUp() public override { diff --git a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol index 760f602df21e..97ef01262ab6 100644 --- a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; // Testing import { stdStorage, StdStorage } from "forge-std/Test.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -19,7 +19,7 @@ import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; -contract L1StandardBridge_Getter_Test is Bridge_Initializer { +contract L1StandardBridge_Getter_Test is CommonTest { /// @dev Test that the accessors return the correct initialized values. function test_getters_succeeds() external view { assert(l1StandardBridge.l2TokenBridge() == address(l2StandardBridge)); @@ -31,7 +31,7 @@ contract L1StandardBridge_Getter_Test is Bridge_Initializer { } } -contract L1StandardBridge_Initialize_Test is Bridge_Initializer { +contract L1StandardBridge_Initialize_Test is CommonTest { /// @dev Test that the constructor sets the correct values. /// @notice Marked virtual to be overridden in /// test/kontrol/deployment/DeploymentSummary.t.sol @@ -58,7 +58,7 @@ contract L1StandardBridge_Initialize_Test is Bridge_Initializer { } } -contract L1StandardBridge_Pause_Test is Bridge_Initializer { +contract L1StandardBridge_Pause_Test is CommonTest { /// @dev Verifies that the `paused` accessor returns the same value as the `paused` function of the /// `superchainConfig`. function test_paused_succeeds() external view { @@ -86,7 +86,7 @@ contract L1StandardBridge_Pause_Test is Bridge_Initializer { } } -contract L1StandardBridge_Pause_TestFail is Bridge_Initializer { +contract L1StandardBridge_Pause_TestFail is CommonTest { /// @dev Sets up the test by pausing the bridge, giving ether to the bridge and mocking /// the calls to the xDomainMessageSender so that it returns the correct value. function setUp() public override { @@ -157,9 +157,9 @@ contract L1StandardBridge_Pause_TestFail is Bridge_Initializer { } } -contract L1StandardBridge_Initialize_TestFail is Bridge_Initializer { } +contract L1StandardBridge_Initialize_TestFail is CommonTest { } -contract L1StandardBridge_Receive_Test is Bridge_Initializer { +contract L1StandardBridge_Receive_Test is CommonTest { /// @dev Tests receive bridges ETH successfully. function test_receive_succeeds() external { assertEq(address(optimismPortal).balance, 0); @@ -190,7 +190,7 @@ contract L1StandardBridge_Receive_Test is Bridge_Initializer { } } -contract L1StandardBridge_Receive_TestFail is Bridge_Initializer { +contract L1StandardBridge_Receive_TestFail is CommonTest { /// @dev Tests receive function reverts with custom gas token. function testFuzz_receive_customGasToken_reverts(uint256 _value) external { vm.prank(alice, alice); @@ -207,7 +207,7 @@ contract L1StandardBridge_Receive_TestFail is Bridge_Initializer { } } -contract PreBridgeETH is Bridge_Initializer { +contract PreBridgeETH is CommonTest { /// @dev Asserts the expected calls and events for bridging ETH depending /// on whether the bridge call is legacy or not. function _preBridgeETH(bool isLegacy, uint256 value) internal { @@ -285,7 +285,7 @@ contract L1StandardBridge_DepositETH_Test is PreBridgeETH { } } -contract L1StandardBridge_DepositETH_TestFail is Bridge_Initializer { +contract L1StandardBridge_DepositETH_TestFail is CommonTest { /// @dev Tests that depositing ETH reverts if the call is not from an EOA. function test_depositETH_notEoa_reverts() external { vm.etch(alice, address(L1Token).code); @@ -331,7 +331,7 @@ contract L1StandardBridge_BridgeETH_TestFail is PreBridgeETH { } } -contract PreBridgeETHTo is Bridge_Initializer { +contract PreBridgeETHTo is CommonTest { /// @dev Asserts the expected calls and events for bridging ETH to a different /// address depending on whether the bridge call is legacy or not. function _preBridgeETHTo(bool isLegacy, uint256 value) internal { @@ -411,7 +411,7 @@ contract L1StandardBridge_DepositETHTo_Test is PreBridgeETHTo { } } -contract L1StandardBridge_DepositETHTo_TestFail is Bridge_Initializer { +contract L1StandardBridge_DepositETHTo_TestFail is CommonTest { /// @dev Tests that depositETHTo reverts with custom gas token. function testFuzz_depositETHTo_customGasToken_reverts( uint256 _value, @@ -463,7 +463,7 @@ contract L1StandardBridge_BridgeETHTo_TestFail is PreBridgeETHTo { } } -contract L1StandardBridge_DepositERC20_Test is Bridge_Initializer { +contract L1StandardBridge_DepositERC20_Test is CommonTest { using stdStorage for StdStorage; // depositERC20 @@ -540,7 +540,7 @@ contract L1StandardBridge_DepositERC20_Test is Bridge_Initializer { } } -contract L1StandardBridge_DepositERC20_TestFail is Bridge_Initializer { +contract L1StandardBridge_DepositERC20_TestFail is CommonTest { /// @dev Tests that depositing an ERC20 to the bridge reverts /// if the caller is not an EOA. function test_depositERC20_notEoa_reverts() external { @@ -553,7 +553,7 @@ contract L1StandardBridge_DepositERC20_TestFail is Bridge_Initializer { } } -contract L1StandardBridge_DepositERC20To_Test is Bridge_Initializer { +contract L1StandardBridge_DepositERC20To_Test is CommonTest { /// @dev Tests that depositing ERC20 to the bridge succeeds when /// sent to a different address. /// Bridge deposits are updated. @@ -622,7 +622,7 @@ contract L1StandardBridge_DepositERC20To_Test is Bridge_Initializer { } } -contract L1StandardBridge_FinalizeETHWithdrawal_Test is Bridge_Initializer { +contract L1StandardBridge_FinalizeETHWithdrawal_Test is CommonTest { using stdStorage for StdStorage; /// @dev Tests that finalizing an ETH withdrawal succeeds. @@ -654,7 +654,7 @@ contract L1StandardBridge_FinalizeETHWithdrawal_Test is Bridge_Initializer { } } -contract L1StandardBridge_FinalizeETHWithdrawal_TestFail is Bridge_Initializer { +contract L1StandardBridge_FinalizeETHWithdrawal_TestFail is CommonTest { /// @dev Tests that finalizeETHWithdrawal reverts with custom gas token. function testFuzz_finalizeETHWithdrawal_customGasToken_reverts( uint256 _value, @@ -678,7 +678,7 @@ contract L1StandardBridge_FinalizeETHWithdrawal_TestFail is Bridge_Initializer { } } -contract L1StandardBridge_FinalizeERC20Withdrawal_Test is Bridge_Initializer { +contract L1StandardBridge_FinalizeERC20Withdrawal_Test is CommonTest { using stdStorage for StdStorage; /// @dev Tests that finalizing an ERC20 withdrawal succeeds. @@ -717,7 +717,7 @@ contract L1StandardBridge_FinalizeERC20Withdrawal_Test is Bridge_Initializer { } } -contract L1StandardBridge_FinalizeERC20Withdrawal_TestFail is Bridge_Initializer { +contract L1StandardBridge_FinalizeERC20Withdrawal_TestFail is CommonTest { /// @dev Tests that finalizing an ERC20 withdrawal reverts if the caller is not the L2 bridge. function test_finalizeERC20Withdrawal_notMessenger_reverts() external { vm.mockCall( @@ -743,7 +743,7 @@ contract L1StandardBridge_FinalizeERC20Withdrawal_TestFail is Bridge_Initializer } } -contract L1StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { +contract L1StandardBridge_FinalizeBridgeETH_Test is CommonTest { /// @dev Tests that finalizing bridged ETH succeeds. function test_finalizeBridgeETH_succeeds() external { address messenger = address(l1StandardBridge.messenger()); @@ -762,7 +762,7 @@ contract L1StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { } } -contract L1StandardBridge_FinalizeBridgeETH_TestFail is Bridge_Initializer { +contract L1StandardBridge_FinalizeBridgeETH_TestFail is CommonTest { /// @dev Tests that finalizing bridged reverts with custom gas token. function testFuzz_finalizeBridgeETH_customGasToken_reverts(uint256 _value, bytes calldata _extraData) external { vm.mockCall( diff --git a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol index bf63a700c098..1864e1d07664 100644 --- a/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/BaseFeeVault.t.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract FeeVault_Test is Bridge_Initializer { +contract FeeVault_Test is CommonTest { /// @dev Tests that the constructor sets the correct values. function test_constructor_baseFeeVault_succeeds() external view { assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol index a80f633fede2..7bdbdaa9f708 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable2.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; @@ -23,7 +23,7 @@ contract XDomainSetter2 is CrossDomainOwnable2 { } } -contract CrossDomainOwnable2_Test is Bridge_Initializer { +contract CrossDomainOwnable2_Test is CommonTest { XDomainSetter2 setter; /// @dev Sets up the test suite. diff --git a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol index 968910478198..e1bac7c784bb 100644 --- a/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; @@ -23,7 +23,7 @@ contract XDomainSetter3 is CrossDomainOwnable3 { } } -contract CrossDomainOwnable3_Test is Bridge_Initializer { +contract CrossDomainOwnable3_Test is CommonTest { XDomainSetter3 setter; /// @dev CrossDomainOwnable3.sol transferOwnership event diff --git a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol index 03a3e7e5ad9b..5284054056d1 100644 --- a/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/L1FeeVault.t.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Types } from "src/libraries/Types.sol"; // Test the implementations of the FeeVault -contract FeeVault_Test is Bridge_Initializer { +contract FeeVault_Test is CommonTest { /// @dev Tests that the constructor sets the correct values. function test_constructor_l1FeeVault_succeeds() external view { assertEq(l1FeeVault.RECIPIENT(), deploy.cfg().l1FeeVaultRecipient()); diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index a2cd9382ca84..131851783c79 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; @@ -16,7 +16,7 @@ import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { IL2CrossDomainMessenger } from "src/L2/interfaces/IL2CrossDomainMessenger.sol"; import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; -contract L2CrossDomainMessenger_Test is Bridge_Initializer { +contract L2CrossDomainMessenger_Test is CommonTest { /// @dev Receiver address for testing address recipient = address(0xabbaacdc); diff --git a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol index 58179cb93207..5e7798824c8d 100644 --- a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; @@ -33,7 +33,7 @@ contract TestMintableERC721 is OptimismMintableERC721 { } } -contract L2ERC721Bridge_Test is Bridge_Initializer { +contract L2ERC721Bridge_Test is CommonTest { TestMintableERC721 internal localToken; TestERC721 internal remoteToken; uint256 internal constant tokenId = 1; diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol index d703ec95f64e..86100b6e4d40 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.15; // Testing import { stdStorage, StdStorage } from "forge-std/Test.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -21,7 +21,7 @@ import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol"; import { IL2ToL1MessagePasser } from "src/L2/interfaces/IL2ToL1MessagePasser.sol"; import { IL2StandardBridge } from "src/L2/interfaces/IL2StandardBridge.sol"; -contract L2StandardBridge_Test is Bridge_Initializer { +contract L2StandardBridge_Test is CommonTest { using stdStorage for StdStorage; /// @dev Test that the bridge's constructor sets the correct values. @@ -275,7 +275,7 @@ contract L2StandardBridge_Test is Bridge_Initializer { } } -contract PreBridgeERC20 is Bridge_Initializer { +contract PreBridgeERC20 is CommonTest { /// @dev Sets up expected calls and emits for a successful ERC20 withdrawal. function _preBridgeERC20(bool _isLegacy, address _l2Token) internal { // Alice has 100 L2Token @@ -401,7 +401,7 @@ contract L2StandardBridge_BridgeERC20_Test is PreBridgeERC20 { } } -contract PreBridgeERC20To is Bridge_Initializer { +contract PreBridgeERC20To is CommonTest { // withdrawTo and BridgeERC20To should behave the same when transferring ERC20 tokens // so they should share the same setup and expectEmit calls function _preBridgeERC20To(bool _isLegacy, address _l2Token) internal { @@ -501,7 +501,7 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To { } } -contract L2StandardBridge_Bridge_Test is Bridge_Initializer { +contract L2StandardBridge_Bridge_Test is CommonTest { /// @dev Tests that `finalizeBridgeETH` reverts if the recipient is the other bridge. function test_finalizeBridgeETH_sendToSelf_reverts() external { vm.mockCall( @@ -622,7 +622,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { } } -contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { +contract L2StandardBridge_FinalizeBridgeETH_Test is CommonTest { /// @dev Tests that `finalizeBridgeETH` succeeds. function test_finalizeBridgeETH_succeeds() external { address messenger = address(l2StandardBridge.messenger()); diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol index fdfd1b0ec6d3..30212d7ad622 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Interfaces import { IMintableAndBurnableERC20 } from "src/L2/interfaces/IMintableAndBurnableERC20.sol"; @@ -13,7 +13,7 @@ import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintab import { ILegacyMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { IOptimismERC20Factory } from "src/L2/interfaces/IOptimismERC20Factory.sol"; -contract L2StandardBridgeInterop_Test is Bridge_Initializer { +contract L2StandardBridgeInterop_Test is CommonTest { /// @notice Emitted when a conversion is made. event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol index 4de8b06b8ee0..8a6a77014698 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Beacon.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -10,7 +10,7 @@ import { IBeacon } from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; /// @title OptimismSuperchainERC20BeaconTest /// @notice Contract for testing the OptimismSuperchainERC20Beacon contract. -contract OptimismSuperchainERC20BeaconTest is Bridge_Initializer { +contract OptimismSuperchainERC20BeaconTest is CommonTest { /// @notice Sets up the test suite. function setUp() public override { super.enableInterop(); diff --git a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Factory.t.sol b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Factory.t.sol index 15630a15d8eb..d8d7f86f26a2 100644 --- a/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Factory.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismSuperchainERC20Factory.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { CREATE3, Bytes32AddressLib } from "@rari-capital/solmate/src/utils/CREATE3.sol"; @@ -13,7 +13,7 @@ import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadat /// @title OptimismSuperchainERC20FactoryTest /// @notice Contract for testing the OptimismSuperchainERC20Factory contract. -contract OptimismSuperchainERC20FactoryTest is Bridge_Initializer { +contract OptimismSuperchainERC20FactoryTest is CommonTest { using Bytes32AddressLib for bytes32; event OptimismSuperchainERC20Created( diff --git a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol index 1dc01ba0008e..3c39e8b1792c 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -17,7 +17,7 @@ import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; /// @title SuperchainTokenBridgeTest /// @notice Contract for testing the SuperchainTokenBridge contract. -contract SuperchainTokenBridgeTest is Bridge_Initializer { +contract SuperchainTokenBridgeTest is CommonTest { address internal constant ZERO_ADDRESS = address(0); string internal constant NAME = "SuperchainERC20"; string internal constant SYMBOL = "OSE"; diff --git a/packages/contracts-bedrock/test/invariants/CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/invariants/CrossDomainMessenger.t.sol index 08a0c0027763..3bf4ad3f7b3a 100644 --- a/packages/contracts-bedrock/test/invariants/CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/invariants/CrossDomainMessenger.t.sol @@ -5,12 +5,11 @@ import { StdUtils } from "forge-std/StdUtils.sol"; import { Vm } from "forge-std/Vm.sol"; import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMessenger.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Encoding } from "src/libraries/Encoding.sol"; import { Hashing } from "src/libraries/Hashing.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; contract RelayActor is StdUtils { // Storage slot of the l2Sender @@ -88,7 +87,7 @@ contract RelayActor is StdUtils { } } -contract XDM_MinGasLimits is Bridge_Initializer { +contract XDM_MinGasLimits is CommonTest { RelayActor actor; function init(bool doFail) public virtual { diff --git a/packages/contracts-bedrock/test/setup/Bridge_Initializer.sol b/packages/contracts-bedrock/test/setup/Bridge_Initializer.sol deleted file mode 100644 index 6b931712935e..000000000000 --- a/packages/contracts-bedrock/test/setup/Bridge_Initializer.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { CommonTest } from "test/setup/CommonTest.sol"; -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; -import { LegacyMintableERC20 } from "src/legacy/LegacyMintableERC20.sol"; - -/// @title Bridge_Initializer -/// @dev This contract extends the CommonTest contract with token deployments -/// meant to be used with the bridge contracts. -contract Bridge_Initializer is CommonTest { - ERC20 L1Token; - ERC20 BadL1Token; - OptimismMintableERC20 L2Token; - LegacyMintableERC20 LegacyL2Token; - ERC20 NativeL2Token; - ERC20 BadL2Token; - OptimismMintableERC20 RemoteL1Token; - - function setUp() public virtual override { - super.setUp(); - - L1Token = new ERC20("Native L1 Token", "L1T"); - - LegacyL2Token = new LegacyMintableERC20({ - _l2Bridge: address(l2StandardBridge), - _l1Token: address(L1Token), - _name: string.concat("LegacyL2-", L1Token.name()), - _symbol: string.concat("LegacyL2-", L1Token.symbol()) - }); - vm.label(address(LegacyL2Token), "LegacyMintableERC20"); - - // Deploy the L2 ERC20 now - L2Token = OptimismMintableERC20( - l2OptimismMintableERC20Factory.createStandardL2Token( - address(L1Token), - string(abi.encodePacked("L2-", L1Token.name())), - string(abi.encodePacked("L2-", L1Token.symbol())) - ) - ); - - BadL2Token = OptimismMintableERC20( - l2OptimismMintableERC20Factory.createStandardL2Token( - address(1), - string(abi.encodePacked("L2-", L1Token.name())), - string(abi.encodePacked("L2-", L1Token.symbol())) - ) - ); - - NativeL2Token = new ERC20("Native L2 Token", "L2T"); - - RemoteL1Token = OptimismMintableERC20( - l1OptimismMintableERC20Factory.createStandardL2Token( - address(NativeL2Token), - string(abi.encodePacked("L1-", NativeL2Token.name())), - string(abi.encodePacked("L1-", NativeL2Token.symbol())) - ) - ); - - BadL1Token = OptimismMintableERC20( - l1OptimismMintableERC20Factory.createStandardL2Token( - address(1), - string(abi.encodePacked("L1-", NativeL2Token.name())), - string(abi.encodePacked("L1-", NativeL2Token.symbol())) - ) - ); - } -} diff --git a/packages/contracts-bedrock/test/setup/CommonTest.sol b/packages/contracts-bedrock/test/setup/CommonTest.sol index 679ce200559c..93ede20b629e 100644 --- a/packages/contracts-bedrock/test/setup/CommonTest.sol +++ b/packages/contracts-bedrock/test/setup/CommonTest.sol @@ -6,7 +6,9 @@ import { Setup } from "test/setup/Setup.sol"; import { Events } from "test/setup/Events.sol"; import { FFIInterface } from "test/setup/FFIInterface.sol"; import { Constants } from "src/libraries/Constants.sol"; -import "scripts/deploy/DeployConfig.s.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; +import { LegacyMintableERC20 } from "src/legacy/LegacyMintableERC20.sol"; /// @title CommonTest /// @dev An extenstion to `Test` that sets up the optimism smart contracts. @@ -23,6 +25,14 @@ contract CommonTest is Test, Setup, Events { address customGasToken; bool useInteropOverride; + ERC20 L1Token; + ERC20 BadL1Token; + OptimismMintableERC20 L2Token; + LegacyMintableERC20 LegacyL2Token; + ERC20 NativeL2Token; + ERC20 BadL2Token; + OptimismMintableERC20 RemoteL1Token; + function setUp() public virtual override { alice = makeAddr("alice"); bob = makeAddr("bob"); @@ -65,6 +75,56 @@ contract CommonTest is Test, Setup, Events { Setup.L1(); // Deploy L2 Setup.L2(); + + // Call bridge initializer setup function + bridgeInitializerSetUp(); + } + + function bridgeInitializerSetUp() public { + L1Token = new ERC20("Native L1 Token", "L1T"); + + LegacyL2Token = new LegacyMintableERC20({ + _l2Bridge: address(l2StandardBridge), + _l1Token: address(L1Token), + _name: string.concat("LegacyL2-", L1Token.name()), + _symbol: string.concat("LegacyL2-", L1Token.symbol()) + }); + vm.label(address(LegacyL2Token), "LegacyMintableERC20"); + + // Deploy the L2 ERC20 now + L2Token = OptimismMintableERC20( + l2OptimismMintableERC20Factory.createStandardL2Token( + address(L1Token), + string(abi.encodePacked("L2-", L1Token.name())), + string(abi.encodePacked("L2-", L1Token.symbol())) + ) + ); + + BadL2Token = OptimismMintableERC20( + l2OptimismMintableERC20Factory.createStandardL2Token( + address(1), + string(abi.encodePacked("L2-", L1Token.name())), + string(abi.encodePacked("L2-", L1Token.symbol())) + ) + ); + + NativeL2Token = new ERC20("Native L2 Token", "L2T"); + + RemoteL1Token = OptimismMintableERC20( + l1OptimismMintableERC20Factory.createStandardL2Token( + address(NativeL2Token), + string(abi.encodePacked("L1-", NativeL2Token.name())), + string(abi.encodePacked("L1-", NativeL2Token.symbol())) + ) + ); + + BadL1Token = OptimismMintableERC20( + l1OptimismMintableERC20Factory.createStandardL2Token( + address(1), + string(abi.encodePacked("L1-", NativeL2Token.name())), + string(abi.encodePacked("L1-", NativeL2Token.symbol())) + ) + ); } /// @dev Helper function that wraps `TransactionDeposited` event. diff --git a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol index a68cccf6862f..fd0a9bfc7847 100644 --- a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol @@ -5,7 +5,6 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; import { Vm } from "forge-std/Vm.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; // Libraries import { Types } from "src/libraries/Types.sol"; @@ -110,7 +109,7 @@ contract GasBenchMark_OptimismPortal is CommonTest { } } -contract GasBenchMark_L1CrossDomainMessenger is Bridge_Initializer { +contract GasBenchMark_L1CrossDomainMessenger is CommonTest { function test_sendMessage_benchmark_0() external { vm.pauseGasMetering(); setPrevBaseFee(vm, address(optimismPortal), 1 gwei); @@ -132,7 +131,7 @@ contract GasBenchMark_L1CrossDomainMessenger is Bridge_Initializer { } } -contract GasBenchMark_L1StandardBridge_Deposit is Bridge_Initializer { +contract GasBenchMark_L1StandardBridge_Deposit is CommonTest { function setUp() public virtual override { super.setUp(); deal(address(L1Token), alice, 100000, true); @@ -181,7 +180,7 @@ contract GasBenchMark_L1StandardBridge_Deposit is Bridge_Initializer { } } -contract GasBenchMark_L1StandardBridge_Finalize is Bridge_Initializer { +contract GasBenchMark_L1StandardBridge_Finalize is CommonTest { function setUp() public virtual override { super.setUp(); deal(address(L1Token), address(l1StandardBridge), 100, true); diff --git a/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol index 065c3e1bc3fc..50398e4a8920 100644 --- a/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; // Testing utilities import { Test } from "forge-std/Test.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -14,7 +14,7 @@ import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMesseng // CrossDomainMessenger_Test is for testing functionality which is common to both the L1 and L2 // CrossDomainMessenger contracts. For simplicity, we use the L1 Messenger as the test contract. -contract CrossDomainMessenger_BaseGas_Test is Bridge_Initializer { +contract CrossDomainMessenger_BaseGas_Test is CommonTest { /// @dev Ensure that baseGas passes for the max value of _minGasLimit, /// this is about 4 Billion. function test_baseGas_succeeds() external view { @@ -110,7 +110,7 @@ contract ExternalRelay is Test { /// @title CrossDomainMessenger_RelayMessage_Test /// @notice Fuzz tests re-entrancy into the CrossDomainMessenger relayMessage function. -contract CrossDomainMessenger_RelayMessage_Test is Bridge_Initializer { +contract CrossDomainMessenger_RelayMessage_Test is CommonTest { // Storage slot of the l2Sender uint256 constant senderSlotIndex = 50; diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol index 18e080372745..d56a97b19db3 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -contract OptimismMintableERC20_Test is Bridge_Initializer { +contract OptimismMintableERC20_Test is CommonTest { event Mint(address indexed account, uint256 amount); event Burn(address indexed account, uint256 amount); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol index d146b050f387..74df9e729e83 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC20Factory.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { NextImpl } from "test/mocks/NextImpl.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; @@ -14,7 +14,7 @@ import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC2 import { IProxy } from "src/universal/interfaces/IProxy.sol"; import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; -contract OptimismMintableTokenFactory_Test is Bridge_Initializer { +contract OptimismMintableTokenFactory_Test is CommonTest { event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol index daea00064cf4..d22e0957a7df 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol @@ -5,10 +5,10 @@ import { ERC721, IERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { OptimismMintableERC721, IOptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; -contract OptimismMintableERC721_Test is Bridge_Initializer { +contract OptimismMintableERC721_Test is CommonTest { ERC721 internal L1NFT; OptimismMintableERC721 internal L2NFT; diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol index ef9019eafa04..91340001c5e9 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721Factory.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; import { OptimismMintableERC721Factory } from "src/universal/OptimismMintableERC721Factory.sol"; -contract OptimismMintableERC721Factory_Test is Bridge_Initializer { +contract OptimismMintableERC721Factory_Test is CommonTest { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); function test_constructor_succeeds() external view { diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 9d60ab71e7c8..eb43ae187599 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; // Scripts import { ForgeArtifacts } from "scripts/libraries/ForgeArtifacts.sol"; @@ -11,8 +11,7 @@ import { Process } from "scripts/libraries/Process.sol"; // Libraries import { LibString } from "@solady/utils/LibString.sol"; import { Constants } from "src/libraries/Constants.sol"; -import "src/dispute/lib/Types.sol"; -import "scripts/deploy/Deployer.sol"; +import { GameType } from "src/dispute/lib/Types.sol"; // Interfaces import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; @@ -26,7 +25,7 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr /// once. This contract inherits from `ERC721Bridge_Initializer` because it is the /// deepest contract in the inheritance chain for setting up the system contracts. /// For each L1 contract both the implementation and the proxy are tested. -contract Initializer_Test is Bridge_Initializer { +contract Initializer_Test is CommonTest { /// @notice Contains the address of an `Initializable` contract and the calldata /// used to initialize it. struct InitializeableContract { From f7cef71158da35c2cb4b16adcb899a4cf77f1ce7 Mon Sep 17 00:00:00 2001 From: Michael de Hoog Date: Thu, 7 Nov 2024 15:39:44 -1000 Subject: [PATCH 157/451] [Batcher] Fix memory leak, reverse prepended blocks (#12874) * Don't prepend blocks in reverse order * Fix memory leak --- op-batcher/batcher/channel_manager.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 25900906d0dd..06403645ea4d 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -118,8 +118,11 @@ func (s *channelManager) TxConfirmed(_id txID, inclusionBlock eth.BlockID) { delete(s.txChannels, id) done, blocksToRequeue := channel.TxConfirmed(id, inclusionBlock) if done { + s.removePendingChannel(channel) + if len(blocksToRequeue) > 0 { + s.blocks.Prepend(blocksToRequeue...) + } for _, b := range blocksToRequeue { - s.blocks.Prepend(b) s.metr.RecordL2BlockInPendingQueue(b) } } @@ -505,8 +508,8 @@ func (s *channelManager) Requeue(newCfg ChannelConfig) { } // We put the blocks back at the front of the queue: + s.blocks.Prepend(blocksToRequeue...) for _, b := range blocksToRequeue { - s.blocks.Prepend(b) s.metr.RecordL2BlockInPendingQueue(b) } From 4ee0737dafd83de22989c5be0ce74397fd9c3a94 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 8 Nov 2024 08:01:44 +0100 Subject: [PATCH 158/451] add semgrep rule sol-style-no-bare-imports and implement it (#12723) * add semgrep rule sol-style-no-bare-imports and implement it * write test for bare imports rule * fix forge fmt on semgrep test * fix bare import * semver lock * fix semver --- .semgrep/rules/sol-rules.yaml | 9 ++++++++ .semgrep/tests/sol-rules.t.sol | 23 +++++++++++++++++++ .../scripts/deploy/Deploy.s.sol | 2 +- packages/contracts-bedrock/semver-lock.json | 4 ++-- .../src/cannon/libraries/MIPS64Memory.sol | 2 +- .../src/cannon/libraries/MIPSMemory.sol | 2 +- .../src/dispute/FaultDisputeGame.sol | 7 +++--- .../interfaces/IAnchorStateRegistry.sol | 2 +- .../src/dispute/interfaces/IDisputeGame.sol | 2 +- .../interfaces/IDisputeGameFactory.sol | 2 +- .../dispute/interfaces/IFaultDisputeGame.sol | 2 +- .../interfaces/IPermissionedDisputeGame.sol | 2 +- .../src/dispute/lib/Errors.sol | 2 +- .../src/dispute/lib/LibUDT.sol | 2 +- .../src/dispute/lib/Types.sol | 14 ++++++++++- .../src/libraries/rlp/RLPReader.sol | 9 +++++++- 16 files changed, 68 insertions(+), 18 deletions(-) diff --git a/.semgrep/rules/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml index e0d83414c4bd..57dc88a3e51e 100644 --- a/.semgrep/rules/sol-rules.yaml +++ b/.semgrep/rules/sol-rules.yaml @@ -186,3 +186,12 @@ rules: paths: exclude: - packages/contracts-bedrock/ + + - id: sol-style-no-bare-imports + languages: [solidity] + severity: ERROR + message: Import specific components instead of the entire file + pattern-regex: import\s+"[^"]+"\s*; + paths: + exclude: + - packages/contracts-bedrock/test diff --git a/.semgrep/tests/sol-rules.t.sol b/.semgrep/tests/sol-rules.t.sol index 3b4329f7e388..650e03198270 100644 --- a/.semgrep/tests/sol-rules.t.sol +++ b/.semgrep/tests/sol-rules.t.sol @@ -7,6 +7,29 @@ // Use comments like "ruleid: " to assert that the rule catches the code. // Use comments like "ok: " to assert that the rule does not catch the code. +/// begin SemgrepTest__sol-style-no-bare-imports +// ok: sol-style-no-bare-imports +import { SomeStruct } from "some-library.sol"; + +// ok: sol-style-no-bare-imports +import { SomeStruct, AnotherThing } from "some-library.sol"; + +// ok: sol-style-no-bare-imports +import { SomeStruct as SomeOtherStruct } from "some-library.sol"; + +// ok: sol-style-no-bare-imports +import { SomeStruct as SomeOtherStruct, AnotherThing as AnotherOtherThing } from "some-library.sol"; + +// ok: sol-style-no-bare-imports +import { SomeStruct as SomeOtherStruct, AnotherThing } from "some-library.sol"; + +// ok: sol-style-no-bare-imports +import { AnotherThing, SomeStruct as SomeOtherStruct } from "some-library.sol"; + +// ruleid: sol-style-no-bare-imports +import "some-library.sol"; +/// end SemgrepTest__sol-style-no-bare-imports + contract SemgrepTest__sol_safety_deployutils_args { function test() { // ruleid: sol-safety-deployutils-args diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 33e1578ea8bb..54cc1a23e172 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -33,7 +33,7 @@ import { Constants } from "src/libraries/Constants.sol"; import { Types } from "scripts/libraries/Types.sol"; import { Duration } from "src/dispute/lib/LibUDT.sol"; import { StorageSlot, ForgeArtifacts } from "scripts/libraries/ForgeArtifacts.sol"; -import "src/dispute/lib/Types.sol"; +import { GameType, Claim, GameTypes, OutputRoot, Hash } from "src/dispute/lib/Types.sol"; // Interfaces import { IProxy } from "src/universal/interfaces/IProxy.sol"; diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 1e14f5d286b5..cb4cc8a55be9 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -168,8 +168,8 @@ "sourceCodeHash": "0x9cb0851b6e471461f2bb369bd72eef4cffe8a0d1345546608a2aa6795540211d" }, "src/dispute/FaultDisputeGame.sol": { - "initCodeHash": "0x1480098a19f71ce6b4f4548880c8794402315ed3efa6793241a3df0fae864205", - "sourceCodeHash": "0x2f084f3f0e52017beb2ecf571178b94ba885cca987ada472d9e178b486a91d9e" + "initCodeHash": "0xa352179f5055232764aac6b66a3ff5a6b3bfae2101d20c077f714b0ed7e40eef", + "sourceCodeHash": "0x730eff9147294c115a0a53e7e75771bcc4a517beb48457140ab929a8d1510893" }, "src/legacy/DeployerWhitelist.sol": { "initCodeHash": "0x0b8177ed75b69eddbb9ce6537683f69a9935efed86a1d6faa8feaafbd151c1bd", diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol index 23a6639bc9c4..9ed97396e10f 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import "src/cannon/libraries/CannonErrors.sol"; +import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; library MIPS64Memory { uint64 internal constant EXT_MASK = 0x7; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol index e88f51438ef4..015955954b5a 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import "src/cannon/libraries/CannonErrors.sol"; +import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; library MIPSMemory { /// @notice Reads a 32-bit value from memory. diff --git a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol index 9466465bd7ff..2bd5ec67e96c 100644 --- a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol @@ -12,17 +12,16 @@ import { GameStatus, GameType, Claim, - Position, Clock, Duration, Timestamp, Hash, OutputRoot, - LibPosition, LibClock, LocalPreimageKey, VMStatuses } from "src/dispute/lib/Types.sol"; +import { Position, LibPosition } from "src/dispute/lib/LibPosition.sol"; import { InvalidParent, ClaimAlreadyExists, @@ -147,8 +146,8 @@ contract FaultDisputeGame is Clone, ISemver { uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8; /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.6 - string public constant version = "1.3.1-beta.6"; + /// @custom:semver 1.3.1-beta.7 + string public constant version = "1.3.1-beta.7"; /// @notice The starting timestamp of the game Timestamp public createdAt; diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IAnchorStateRegistry.sol b/packages/contracts-bedrock/src/dispute/interfaces/IAnchorStateRegistry.sol index 4de2bb1deab6..4c79c4c092a9 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IAnchorStateRegistry.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IAnchorStateRegistry.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -import "src/dispute/lib/Types.sol"; +import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol"; interface IAnchorStateRegistry { struct StartingAnchorRoot { diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol b/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol index f5a650202d0c..0f860e68b5a1 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { IInitializable } from "src/dispute/interfaces/IInitializable.sol"; -import "src/dispute/lib/Types.sol"; +import { Timestamp, GameStatus, GameType, Claim, Hash } from "src/dispute/lib/Types.sol"; interface IDisputeGame is IInitializable { event Resolved(GameStatus indexed status); diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol b/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol index 0f21d42aa27a..3e7233f440d6 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; -import "src/dispute/lib/Types.sol"; +import { GameId, Timestamp, Claim, Hash, GameType } from "src/dispute/lib/Types.sol"; interface IDisputeGameFactory { struct GameSearchResult { diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol index ec0f86ff709c..8c5bac02e9ba 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol @@ -6,7 +6,7 @@ import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; import { Types } from "src/libraries/Types.sol"; -import "src/dispute/lib/Types.sol"; +import { GameType, Claim, Position, Clock, Hash, Duration } from "src/dispute/lib/Types.sol"; interface IFaultDisputeGame is IDisputeGame { struct ClaimData { diff --git a/packages/contracts-bedrock/src/dispute/interfaces/IPermissionedDisputeGame.sol b/packages/contracts-bedrock/src/dispute/interfaces/IPermissionedDisputeGame.sol index 980d3460c048..c5a5a187ec12 100644 --- a/packages/contracts-bedrock/src/dispute/interfaces/IPermissionedDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/interfaces/IPermissionedDisputeGame.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { Types } from "src/libraries/Types.sol"; -import "src/dispute/lib/Types.sol"; +import { GameType, Claim, Position, Clock, Hash, Duration } from "src/dispute/lib/Types.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; diff --git a/packages/contracts-bedrock/src/dispute/lib/Errors.sol b/packages/contracts-bedrock/src/dispute/lib/Errors.sol index 6cd1d1d073f4..6e95bfa5a342 100644 --- a/packages/contracts-bedrock/src/dispute/lib/Errors.sol +++ b/packages/contracts-bedrock/src/dispute/lib/Errors.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; -import "src/dispute/lib/LibUDT.sol"; +import { GameType, Hash, Claim } from "src/dispute/lib/LibUDT.sol"; //////////////////////////////////////////////////////////////// // `DisputeGameFactory` Errors // diff --git a/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol b/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol index b0b8edfab8f4..bfc110f09526 100644 --- a/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol +++ b/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; -import "src/dispute/lib/LibPosition.sol"; +import { Position } from "src/dispute/lib/LibPosition.sol"; using LibClaim for Claim global; using LibHash for Hash global; diff --git a/packages/contracts-bedrock/src/dispute/lib/Types.sol b/packages/contracts-bedrock/src/dispute/lib/Types.sol index 8d86e6e25387..70df7b7912ba 100644 --- a/packages/contracts-bedrock/src/dispute/lib/Types.sol +++ b/packages/contracts-bedrock/src/dispute/lib/Types.sol @@ -1,7 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; -import "src/dispute/lib/LibUDT.sol"; +import { + Position, + Hash, + GameType, + VMStatus, + Timestamp, + Duration, + Clock, + GameId, + Claim, + LibGameId, + LibClock +} from "src/dispute/lib/LibUDT.sol"; /// @notice The current status of the dispute game. enum GameStatus { diff --git a/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol b/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol index 9f82ca8c802e..559c53391f70 100644 --- a/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol +++ b/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol @@ -1,7 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.8; -import "./RLPErrors.sol"; +import { + EmptyItem, + UnexpectedString, + InvalidDataRemainder, + ContentLengthMismatch, + InvalidHeader, + UnexpectedList +} from "./RLPErrors.sol"; /// @custom:attribution https://github.com/hamdiallam/Solidity-RLP /// @title RLPReader From eb977a8e168354288e730e037e0ed43f47d461f7 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 8 Nov 2024 08:02:56 +0100 Subject: [PATCH 159/451] update check-foundry-install.sh to check-dep-install.sh (#12778) * add semgrep check to checks and rename check-foundry-install to check-dep-install * fixes * fixes --- justfile | 2 +- .../scripts/check-foundry.sh | 5 +---- packages/contracts-bedrock/justfile | 14 +++++++++++--- 3 files changed, 13 insertions(+), 8 deletions(-) rename packages/contracts-bedrock/scripts/checks/check-foundry-install.sh => ops/scripts/check-foundry.sh (84%) diff --git a/justfile b/justfile index 00a272e899ae..e40186f75dff 100644 --- a/justfile +++ b/justfile @@ -19,7 +19,7 @@ update-foundry: bash ./ops/scripts/install-foundry.sh check-foundry: - bash ./packages/contracts-bedrock/scripts/checks/check-foundry-install.sh + bash ./ops/scripts/check-foundry.sh install-kontrol: curl -L https://kframework.org/install | bash && just update-kontrol diff --git a/packages/contracts-bedrock/scripts/checks/check-foundry-install.sh b/ops/scripts/check-foundry.sh similarity index 84% rename from packages/contracts-bedrock/scripts/checks/check-foundry-install.sh rename to ops/scripts/check-foundry.sh index a2093e936f3f..530046bd85ea 100755 --- a/packages/contracts-bedrock/scripts/checks/check-foundry-install.sh +++ b/ops/scripts/check-foundry.sh @@ -1,9 +1,6 @@ #!/usr/bin/env bash -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -CONTRACTS_BASE=$(dirname "$(dirname "$SCRIPT_DIR")") -MONOREPO_BASE=$(dirname "$(dirname "$CONTRACTS_BASE")") -VERSIONS_FILE="${MONOREPO_BASE}/versions.json" +VERSIONS_FILE="versions.json" if ! command -v jq &> /dev/null then diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 9be9bc4e09ea..3536e0b8d264 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -16,15 +16,23 @@ dep-status: ######################################################## # Checks that the correct version of Foundry is installed. -prebuild: - ./scripts/checks/check-foundry-install.sh +check-foundry: + cd ../../ && ./ops/scripts/check-foundry.sh + +# Checks that semgrep is installed. +check-semgrep: + cd ../../ && just check-semgrep + +# Checks that the correct versions of Foundry and semgrep are installed. +check-dependencies: + just check-foundry && just check-semgrep # Core forge build command forge-build: forge build # Builds the contracts. -build: prebuild lint-fix-no-fail forge-build interfaces-check-no-build +build: check-dependencies lint-fix-no-fail forge-build interfaces-check-no-build # Builds the go-ffi tool for contract tests. build-go-ffi-default: From 8df2b1d5beed782e8630eb3af403aeebac8497c5 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 8 Nov 2024 08:25:10 +0100 Subject: [PATCH 160/451] convert semver lock to go (#12775) * convert semver lock to go * updae just file * delete old semver lock file * fixes * move snapshot files to snapshots folder * fixes * fix check-semver-diff --- packages/contracts-bedrock/foundry.toml | 1 - packages/contracts-bedrock/justfile | 8 +- .../scripts/autogen/SemverLock.s.sol | 65 --------- .../autogen/generate-semver-lock/main.go | 128 ++++++++++++++++++ .../scripts/checks/check-semver-diff.sh | 9 +- .../scripts/ops/calculate-checksum.sh | 2 +- .../{ => snapshots}/.gas-snapshot | 0 .../{ => snapshots}/semver-lock.json | 0 8 files changed, 140 insertions(+), 73 deletions(-) delete mode 100644 packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol create mode 100644 packages/contracts-bedrock/scripts/autogen/generate-semver-lock/main.go mode change 100644 => 100755 packages/contracts-bedrock/scripts/ops/calculate-checksum.sh rename packages/contracts-bedrock/{ => snapshots}/.gas-snapshot (100%) rename packages/contracts-bedrock/{ => snapshots}/semver-lock.json (100%) diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index b2ea03b86538..6bd441ca3cdb 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -48,7 +48,6 @@ fs_permissions = [ { access='read', path='./deploy-config-periphery/' }, { access='read', path='./broadcast/' }, { access='read', path = './forge-artifacts/' }, - { access='write', path='./semver-lock.json' }, { access='read-write', path='./.testdata/' }, { access='read', path='./kout-deployment' }, { access='read', path='./test/fixtures' }, diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 3536e0b8d264..fe6f4c0834c9 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -96,7 +96,7 @@ deploy: # Generates a gas snapshot without building. gas-snapshot-no-build: - forge snapshot --match-contract GasBenchMark + forge snapshot --match-contract GasBenchMark --snap snapshots/.gas-snapshot # Generates a gas snapshot. gas-snapshot: build-go-ffi gas-snapshot-no-build @@ -116,9 +116,9 @@ kontrol-summary-full: kontrol-summary kontrol-summary-fp snapshots-abi-storage: go run ./scripts/autogen/generate-snapshots . -# Updates the semver-lock.json file. +# Updates the snapshots/semver-lock.json file. semver-lock: - forge script scripts/autogen/SemverLock.s.sol + go run scripts/autogen/generate-semver-lock/main.go # Generates core snapshots without building contracts. Currently just an alias for # snapshots-abi-storage because we no longer run Kontrol snapshots here. Run @@ -135,7 +135,7 @@ snapshots: build snapshots-no-build # Checks that the gas snapshot is up to date without building. gas-snapshot-check-no-build: - forge snapshot --match-contract GasBenchMark --check + forge snapshot --match-contract GasBenchMark --snap snapshots/.gas-snapshot --check # Checks that the gas snapshot is up to date. gas-snapshot-check: build-go-ffi gas-snapshot-check-no-build diff --git a/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol b/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol deleted file mode 100644 index 00b0f1855fb1..000000000000 --- a/packages/contracts-bedrock/scripts/autogen/SemverLock.s.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { Script } from "forge-std/Script.sol"; -import { stdJson } from "forge-std/StdJson.sol"; -import { console2 as console } from "forge-std/console2.sol"; -import { Process } from "scripts/libraries/Process.sol"; - -contract SemverLock is Script { - function run() public { - // First, find all contracts with a Semver inheritance. - string memory rawFiles = - Process.bash("grep -rl '@custom:semver' src | jq -Rs 'split(\"\\n\") | map(select(length > 0))'"); - - string[] memory files = vm.parseJsonStringArray(rawFiles, ""); - writeSemverLock(files); - } - - /// @dev Writes a Semver lockfile - function writeSemverLock(string[] memory _files) internal { - string memory out; - for (uint256 i; i < _files.length; i++) { - // Use FFI to read the file to remove the need for FS permissions in the foundry.toml. - string[] memory commands = new string[](2); - commands[0] = "cat"; - commands[1] = _files[i]; - string memory fileContents = string(Process.run(commands)); - - // Grab the contract name - string memory contractName = - Process.bash(string.concat("echo \"", _files[i], "\"| sed -E 's|src/.*/(.+)\\.sol|\\1|'")); - - string memory artifactsDir = Process.bash("forge config --json | jq -r .out"); - - // Handle the case where there are multiple artifacts for a contract. This happens - // when the same contract is compiled with multiple compiler versions. - string memory contractArtifactDir = string.concat(artifactsDir, "/", contractName, ".sol"); - string memory artifactFiles = Process.bash( - string.concat( - "ls -1 --color=never ", - contractArtifactDir, - " | jq -R -s -c 'split(\"\n\") | map(select(length > 0))'" - ) - ); - - string[] memory files = stdJson.readStringArray(artifactFiles, ""); - require(files.length > 0, string.concat("SemverLock: no artifacts found for ", contractName)); - string memory fileName = files[0]; - - // Parse the artifact to get the contract's initcode hash. - bytes memory initCode = vm.getCode(string.concat(artifactsDir, "/", contractName, ".sol/", fileName)); - - // Serialize the initcode hash + sourcecode hash in JSON. - vm.serializeBytes32(_files[i], "initCodeHash", keccak256(initCode)); - string memory obj = vm.serializeBytes32(_files[i], "sourceCodeHash", keccak256(bytes(fileContents))); - - // Serialize the map from the file name -> initcode hash + sourcecode hash in JSON. - out = vm.serializeString("", _files[i], obj); - } - - // Write the semver lockfile. - vm.writeJson(out, "semver-lock.json"); - console.logString("Wrote semver lock file to \"semver-lock.json\"."); - } -} diff --git a/packages/contracts-bedrock/scripts/autogen/generate-semver-lock/main.go b/packages/contracts-bedrock/scripts/autogen/generate-semver-lock/main.go new file mode 100644 index 000000000000..162f81c840dd --- /dev/null +++ b/packages/contracts-bedrock/scripts/autogen/generate-semver-lock/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + + "github.com/ethereum/go-ethereum/crypto" +) + +const semverLockFile = "snapshots/semver-lock.json" + +func main() { + if err := run(); err != nil { + panic(err) + } +} + +func run() error { + // Find semver files + // Execute grep command to find files with @custom:semver + var cmd = exec.Command("bash", "-c", "grep -rl '@custom:semver' src | jq -Rs 'split(\"\n\") | map(select(length > 0))'") + cmdOutput, err := cmd.Output() + if err != nil { + return err + } + + // Parse the JSON array of files + var files []string + if err := json.Unmarshal(cmdOutput, &files); err != nil { + return fmt.Errorf("failed to parse JSON output: %w", err) + } + + // Hash and write to JSON file + // Map to store our JSON output + output := make(map[string]map[string]string) + + // regex to extract contract name from file path + re := regexp.MustCompile(`src/.*/(.+)\.sol`) + + // Get artifacts directory + cmd = exec.Command("forge", "config", "--json") + out, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to get forge config: %w", err) + } + var config struct { + Out string `json:"out"` + } + if err := json.Unmarshal(out, &config); err != nil { + return fmt.Errorf("failed to parse forge config: %w", err) + } + + for _, file := range files { + // Read file contents + fileContents, err := os.ReadFile(file) + if err != nil { + return fmt.Errorf("failed to read file %s: %w", file, err) + } + + // Extract contract name from file path using regex + matches := re.FindStringSubmatch(file) + if len(matches) < 2 { + return fmt.Errorf("invalid file path format: %s", file) + } + contractName := matches[1] + + // Get artifact files + artifactDir := filepath.Join(config.Out, contractName+".sol") + files, err := os.ReadDir(artifactDir) + if err != nil { + return fmt.Errorf("failed to read artifact directory: %w", err) + } + if len(files) == 0 { + return fmt.Errorf("no artifacts found for %s", contractName) + } + + // Read initcode from artifact + artifactPath := filepath.Join(artifactDir, files[0].Name()) + artifact, err := os.ReadFile(artifactPath) + if err != nil { + return fmt.Errorf("failed to read initcode: %w", err) + } + artifactJson := json.RawMessage(artifact) + var artifactObj struct { + Bytecode struct { + Object string `json:"object"` + } `json:"bytecode"` + } + if err := json.Unmarshal(artifactJson, &artifactObj); err != nil { + return fmt.Errorf("failed to parse artifact: %w", err) + } + + // convert the hex bytecode to a uint8 array / bytes + initCodeBytes, err := hex.DecodeString(strings.TrimPrefix(artifactObj.Bytecode.Object, "0x")) + if err != nil { + return fmt.Errorf("failed to decode hex: %w", err) + } + + // Calculate hashes using Keccak256 + var sourceCode = []byte(strings.TrimSuffix(string(fileContents), "\n")) + initCodeHash := fmt.Sprintf("0x%x", crypto.Keccak256Hash(initCodeBytes)) + sourceCodeHash := fmt.Sprintf("0x%x", crypto.Keccak256Hash(sourceCode)) + + // Store in output map + output[file] = map[string]string{ + "initCodeHash": initCodeHash, + "sourceCodeHash": sourceCodeHash, + } + } + + // Write to JSON file + jsonData, err := json.MarshalIndent(output, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal JSON: %w", err) + } + if err := os.WriteFile(semverLockFile, jsonData, 0644); err != nil { + return fmt.Errorf("failed to write semver lock file: %w", err) + } + + fmt.Printf("Wrote semver lock file to \"%s\".\n", semverLockFile) + return nil +} diff --git a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh index 81e7c6476d3a..62872e1e73d8 100755 --- a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh +++ b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh @@ -9,7 +9,7 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) source "$SCRIPT_DIR/utils/semver-utils.sh" # Path to semver-lock.json. -SEMVER_LOCK="semver-lock.json" +SEMVER_LOCK="snapshots/semver-lock.json" # Create a temporary directory. temp_dir=$(mktemp -d) @@ -22,7 +22,12 @@ if ! { git diff origin/develop...HEAD --name-only; git diff --name-only; git dif fi # Get the upstream semver-lock.json. -git show origin/develop:packages/contracts-bedrock/semver-lock.json > "$temp_dir/upstream_semver_lock.json" +if ! git show origin/develop:packages/contracts-bedrock/snapshots/semver-lock.json > "$temp_dir/upstream_semver_lock.json" 2>/dev/null; then + if ! git show origin/develop:packages/contracts-bedrock/semver-lock.json > "$temp_dir/upstream_semver_lock.json" 2>/dev/null; then + echo "❌ Error: Could not find semver-lock.json in either snapshots/ or root directory of develop branch" + exit 1 + fi +fi # Copy the local semver-lock.json. cp "$SEMVER_LOCK" "$temp_dir/local_semver_lock.json" diff --git a/packages/contracts-bedrock/scripts/ops/calculate-checksum.sh b/packages/contracts-bedrock/scripts/ops/calculate-checksum.sh old mode 100644 new mode 100755 index 86fb4394c091..f00fa6b17641 --- a/packages/contracts-bedrock/scripts/ops/calculate-checksum.sh +++ b/packages/contracts-bedrock/scripts/ops/calculate-checksum.sh @@ -14,7 +14,7 @@ cd "$CONTRACTS_DIR" echoerr "> Calculating contracts checksum..." find . -type f -name '*.sol' -exec sha256sum {} + > manifest.txt -sha256sum semver-lock.json >> manifest.txt +sha256sum snapshots/semver-lock.json >> manifest.txt sha256sum foundry.toml >> manifest.txt # need to specify the locale to ensure consistent sorting across platforms LC_ALL=C sort -o manifest.txt manifest.txt diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/snapshots/.gas-snapshot similarity index 100% rename from packages/contracts-bedrock/.gas-snapshot rename to packages/contracts-bedrock/snapshots/.gas-snapshot diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json similarity index 100% rename from packages/contracts-bedrock/semver-lock.json rename to packages/contracts-bedrock/snapshots/semver-lock.json From 85c9a9ea4cf74adf562fb54ceeb7631407074177 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 8 Nov 2024 09:58:51 +0100 Subject: [PATCH 161/451] fix test name checks (#12880) --- packages/contracts-bedrock/test/L2/Preinstalls.t.sol | 2 +- packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol | 2 +- packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol | 2 +- packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol index a9202346be6a..5eec3f811dec 100644 --- a/packages/contracts-bedrock/test/L2/Preinstalls.t.sol +++ b/packages/contracts-bedrock/test/L2/Preinstalls.t.sol @@ -119,7 +119,7 @@ contract PreinstallsTest is CommonTest { assertPreinstall(Preinstalls.CreateX, Preinstalls.CreateXCode); } - function test_createX_runtimeBytecodeHash() external view { + function test_createX_runtimeBytecodeHash_works() external view { bytes memory createXRuntimeBytecode = Preinstalls.CreateX.code; bytes32 createXRuntimeBytecodeHash = keccak256(createXRuntimeBytecode); diff --git a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol index a9aa3b501618..87f723a9345b 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol @@ -125,7 +125,7 @@ contract SuperchainERC20Test is Test { /// @notice Tests that the `supportsInterface` function returns false for any other interface than the /// `IERC7802` one. - function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { + function testFuzz_supportInterface_works(bytes4 _interfaceId) public view { vm.assume(_interfaceId != type(IERC165).interfaceId); vm.assume(_interfaceId != type(IERC7802).interfaceId); vm.assume(_interfaceId != type(IERC20).interfaceId); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index d0744ecb48ef..e342a53b6026 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -469,7 +469,7 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Tests that the `supportsInterface` function returns false for any other interface than the /// `IERC7802` one. - function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view { + function testFuzz_supportInterface_works(bytes4 _interfaceId) public view { vm.assume(_interfaceId != type(IERC165).interfaceId); vm.assume(_interfaceId != type(IERC7802).interfaceId); vm.assume(_interfaceId != type(IERC20).interfaceId); diff --git a/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol b/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol index e48a9b37a9f8..d7cdc69f37e7 100644 --- a/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployAltDA.t.sol @@ -44,7 +44,7 @@ contract DeployAltDAInput_Test is Test { assertEq(resolverRefundPercentage, dai.resolverRefundPercentage(), "700"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { bytes memory expectedErr = "DeployAltDAInput: "; vm.expectRevert(abi.encodePacked(expectedErr, "salt not set")); @@ -119,7 +119,7 @@ contract DeployAltDAOutput_Test is Test { assertEq(address(dataAvailabilityChallengeImpl), address(dao.dataAvailabilityChallengeImpl()), "200"); } - function test_getters_whenNotSet_revert() public { + function test_getters_whenNotSet_reverts() public { vm.expectRevert("DeployUtils: zero address"); dao.dataAvailabilityChallengeProxy(); @@ -231,7 +231,7 @@ contract DeployAltDA_Test is Test { deployer.checkOutput(dai, dao); } - function testFuzz_run_withDifferentParameters( + function testFuzz_run_withDifferentParameters_works( uint256 _challengeWindow, uint256 _resolveWindow, uint256 _bondSize, From 4c656b3a1638076aeaf3ee999f6e03dd796ed385 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Fri, 8 Nov 2024 05:19:35 -0700 Subject: [PATCH 162/451] op-e2e: Minor test updates (#12877) Attempt at fixing the ongoing test flakiness with the batcher tests. Makes two changes: - Waits for the L1 to be up for all end-to-end tests to mitigate the I/O and context timeouts we've been seeing. - Update the multi batcher test to use an algorithm that's more tolerant of when the L1 doesn't immediately include the transaction. --- op-e2e/e2eutils/wait/waits.go | 26 +++++++++++++++++++++ op-e2e/system/da/multi_test.go | 42 +++++++++++++++++++++------------- op-e2e/system/e2esys/setup.go | 10 ++++++++ 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/op-e2e/e2eutils/wait/waits.go b/op-e2e/e2eutils/wait/waits.go index 3c0e29466cea..9b03e0bba7f9 100644 --- a/op-e2e/e2eutils/wait/waits.go +++ b/op-e2e/e2eutils/wait/waits.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -164,3 +166,27 @@ func AndGet[T interface{}](ctx context.Context, pollRate time.Duration, get func } } } + +func ForNodeUp(ctx context.Context, client *ethclient.Client, lgr log.Logger) error { + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + // Create a new context deliberately. The shorter timeout is used to detect + // potential I/O timeouts on the RPC so we can retry. + callCtx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + _, err := client.BlockNumber(callCtx) + cancel() + if err == nil { + lgr.Info("node is up") + return nil + } + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, os.ErrDeadlineExceeded) { + lgr.Info("timeout waiting for node come up, trying again") + continue + } + return err + } + } +} diff --git a/op-e2e/system/da/multi_test.go b/op-e2e/system/da/multi_test.go index 3d150010a0f3..461270282008 100644 --- a/op-e2e/system/da/multi_test.go +++ b/op-e2e/system/da/multi_test.go @@ -32,10 +32,8 @@ func TestBatcherMultiTx(t *testing.T) { _, err = geth.WaitForBlock(big.NewInt(10), l2Seq) require.NoError(t, err, "Waiting for L2 blocks") - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - l1Number, err := l1Client.BlockNumber(ctx) - require.NoError(t, err) // start batch submission driver := sys.BatchSubmitter.TestDriver() @@ -43,21 +41,33 @@ func TestBatcherMultiTx(t *testing.T) { require.NoError(t, err) totalBatcherTxsCount := int64(0) - // wait for up to 5 L1 blocks, usually only 3 is required, but it's - // possible additional L1 blocks will be created before the batcher starts, - // so we wait additional blocks. - for i := int64(0); i < 5; i++ { - block, err := geth.WaitForBlock(big.NewInt(int64(l1Number)+i), l1Client) - require.NoError(t, err, "Waiting for l1 blocks") - // there are possibly other services (proposer/challenger) in the background sending txs - // so we only count the batcher txs - batcherTxCount, err := transactions.TransactionsBySender(block, cfg.DeployConfig.BatchSenderAddress) - require.NoError(t, err) - totalBatcherTxsCount += int64(batcherTxCount) - if totalBatcherTxsCount >= 10 { - return + headNum, err := l1Client.BlockNumber(ctx) + require.NoError(t, err) + stopNum := headNum + 10 + startBlock := uint64(1) + + for { + for i := startBlock; i <= headNum; i++ { + block, err := l1Client.BlockByNumber(ctx, big.NewInt(int64(i))) + require.NoError(t, err) + + batcherTxCount, err := transactions.TransactionsBySender(block, cfg.DeployConfig.BatchSenderAddress) + require.NoError(t, err) + totalBatcherTxsCount += batcherTxCount + + if totalBatcherTxsCount >= 10 { + return + } + } + + headNum++ + if headNum > stopNum { + break } + startBlock = headNum + _, err = geth.WaitForBlock(big.NewInt(int64(headNum)), l1Client) + require.NoError(t, err) } t.Fatal("Expected at least 10 transactions from the batcher") diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index 9ab6d2de14f6..a54a46d1e5db 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -16,6 +16,8 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/stretchr/testify/require" "golang.org/x/exp/maps" @@ -671,6 +673,14 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, return nil, err } + sysLogger := testlog.Logger(t, log.LevelInfo).New("role", "system") + + l1UpCtx, l1UpCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer l1UpCancel() + if err := wait.ForNodeUp(l1UpCtx, sys.NodeClient(RoleL1), sysLogger); err != nil { + return nil, fmt.Errorf("l1 never came up: %w", err) + } + // Ordered such that the Sequencer is initialized first. Setup this way so that // the `RollupSequencerHTTP` GethOption can be supplied to any sentry nodes. l2Nodes := []string{RoleSeq} From 5662448279e4fb16e073e00baeb6e458b12a59b2 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 8 Nov 2024 19:51:12 +0700 Subject: [PATCH 163/451] op-conductor,op-node: allow system to select port, make op-node wait for conductor endpoint (#12863) * op-conductor,op-node: allow system to select port, make op-node wait for conductor endpoint * op-conductor,op-node: debugging conductor test * op-conductor: more debugging * op-e2e: increase conductor timeout --- op-conductor/conductor/config.go | 18 +- op-conductor/conductor/service.go | 17 +- op-conductor/conductor/service_test.go | 2 +- op-conductor/consensus/iface.go | 3 + op-conductor/consensus/mocks/Consensus.go | 47 +++++- op-conductor/consensus/raft.go | 66 +++++++- op-conductor/consensus/raft_test.go | 4 +- op-conductor/flags/flags.go | 11 +- .../conductor/sequencer_failover_setup.go | 158 ++++++++---------- .../conductor/sequencer_failover_test.go | 1 - op-node/node/conductor.go | 45 +++-- op-node/node/config.go | 5 +- op-node/node/node.go | 1 + op-node/service.go | 9 +- 14 files changed, 258 insertions(+), 129 deletions(-) diff --git a/op-conductor/conductor/config.go b/op-conductor/conductor/config.go index ca18de7d1a18..98e3ad83440e 100644 --- a/op-conductor/conductor/config.go +++ b/op-conductor/conductor/config.go @@ -19,12 +19,19 @@ import ( ) type Config struct { - // ConsensusAddr is the address to listen for consensus connections. + // ConsensusAddr is the address, excluding port, to listen on for consensus connections. + // E.g. 0.0.0.0 to bind to the external-facing network interface. ConsensusAddr string - // ConsensusPort is the port to listen for consensus connections. + // ConsensusPort is the port to listen on for consensus connections. + // If 0, the server binds to a port selected by the system. ConsensusPort int + // ConsensusAdvertisedAddr is the network address, including port, to advertise to other peers. + // This is optional: if empty, the address that the server network transport binds to is used instead. + // E.g. local tests may use temporary addresses, rather than preset known addresses. + ConsensusAdvertisedAddr string + // RaftServerID is the unique ID for this server used by raft consensus. RaftServerID string @@ -117,8 +124,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*Config, error) { } return &Config{ - ConsensusAddr: ctx.String(flags.ConsensusAddr.Name), - ConsensusPort: ctx.Int(flags.ConsensusPort.Name), + ConsensusAddr: ctx.String(flags.ConsensusAddr.Name), + ConsensusPort: ctx.Int(flags.ConsensusPort.Name), + // The consensus server will advertise the address it binds to if this is empty/unspecified. + ConsensusAdvertisedAddr: ctx.String(flags.AdvertisedFullAddr.Name), + RaftBootstrap: ctx.Bool(flags.RaftBootstrap.Name), RaftServerID: ctx.String(flags.RaftServerID.Name), RaftStorageDir: ctx.String(flags.RaftStorageDir.Name), diff --git a/op-conductor/conductor/service.go b/op-conductor/conductor/service.go index 89656fd727aa..cccba2c76ac9 100644 --- a/op-conductor/conductor/service.go +++ b/op-conductor/conductor/service.go @@ -169,10 +169,12 @@ func (c *OpConductor) initConsensus(ctx context.Context) error { return nil } - serverAddr := fmt.Sprintf("%s:%d", c.cfg.ConsensusAddr, c.cfg.ConsensusPort) raftConsensusConfig := &consensus.RaftConsensusConfig{ - ServerID: c.cfg.RaftServerID, - ServerAddr: serverAddr, + ServerID: c.cfg.RaftServerID, + // AdvertisedAddr may be empty: the server will then default to what it binds to. + AdvertisedAddr: raft.ServerAddress(c.cfg.ConsensusAdvertisedAddr), + ListenAddr: c.cfg.ConsensusAddr, + ListenPort: c.cfg.ConsensusPort, StorageDir: c.cfg.RaftStorageDir, Bootstrap: c.cfg.RaftBootstrap, RollupCfg: &c.cfg.RollupCfg, @@ -472,6 +474,12 @@ func (oc *OpConductor) Paused() bool { return oc.paused.Load() } +// ConsensusEndpoint returns the raft consensus server address to connect to. +func (oc *OpConductor) ConsensusEndpoint() string { + return oc.cons.Addr() +} + +// HTTPEndpoint returns the HTTP RPC endpoint func (oc *OpConductor) HTTPEndpoint() string { if oc.rpcServer == nil { return "" @@ -613,7 +621,8 @@ func (oc *OpConductor) handleHealthUpdate(hcerr error) { oc.queueAction() } - if oc.healthy.Swap(healthy) != healthy { + if old := oc.healthy.Swap(healthy); old != healthy { + oc.log.Info("Health state changed", "old", old, "new", healthy) // queue an action if health status changed. oc.queueAction() } diff --git a/op-conductor/conductor/service_test.go b/op-conductor/conductor/service_test.go index 49a05e902763..87df417a4683 100644 --- a/op-conductor/conductor/service_test.go +++ b/op-conductor/conductor/service_test.go @@ -30,7 +30,7 @@ func mockConfig(t *testing.T) Config { now := uint64(time.Now().Unix()) return Config{ ConsensusAddr: "127.0.0.1", - ConsensusPort: 50050, + ConsensusPort: 0, RaftServerID: "SequencerA", RaftStorageDir: "/tmp/raft", RaftBootstrap: false, diff --git a/op-conductor/consensus/iface.go b/op-conductor/consensus/iface.go index 69b9506c50b2..e0dcb6efd5a7 100644 --- a/op-conductor/consensus/iface.go +++ b/op-conductor/consensus/iface.go @@ -42,6 +42,9 @@ type ServerInfo struct { // //go:generate mockery --name Consensus --output mocks/ --with-expecter=true type Consensus interface { + // Addr returns the address of this consensus server. + // Internally the server may override what is advertised, or fall back to the address it listens to. + Addr() string // AddVoter adds a voting member into the cluster, voter is eligible to become leader. // If version is non-zero, this will only be applied if the current cluster version matches the expected version. AddVoter(id, addr string, version uint64) error diff --git a/op-conductor/consensus/mocks/Consensus.go b/op-conductor/consensus/mocks/Consensus.go index ca1397a690e1..902174435146 100644 --- a/op-conductor/consensus/mocks/Consensus.go +++ b/op-conductor/consensus/mocks/Consensus.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. +// Code generated by mockery v2.46.0. DO NOT EDIT. package mocks @@ -118,6 +118,51 @@ func (_c *Consensus_AddVoter_Call) RunAndReturn(run func(string, string, uint64) return _c } +// Addr provides a mock function with given fields: +func (_m *Consensus) Addr() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Addr") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Consensus_Addr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Addr' +type Consensus_Addr_Call struct { + *mock.Call +} + +// Addr is a helper method to define mock.On call +func (_e *Consensus_Expecter) Addr() *Consensus_Addr_Call { + return &Consensus_Addr_Call{Call: _e.mock.On("Addr")} +} + +func (_c *Consensus_Addr_Call) Run(run func()) *Consensus_Addr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Consensus_Addr_Call) Return(_a0 string) *Consensus_Addr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Consensus_Addr_Call) RunAndReturn(run func() string) *Consensus_Addr_Call { + _c.Call.Return(run) + return _c +} + // ClusterMembership provides a mock function with given fields: func (_m *Consensus) ClusterMembership() (*consensus.ClusterMembership, error) { ret := _m.Called() diff --git a/op-conductor/consensus/raft.go b/op-conductor/consensus/raft.go index f6acc0fb76f1..86b32ea00f8c 100644 --- a/op-conductor/consensus/raft.go +++ b/op-conductor/consensus/raft.go @@ -29,12 +29,30 @@ type RaftConsensus struct { serverID raft.ServerID r *raft.Raft + transport *raft.NetworkTransport + // advertisedAddr is the host & port to contact this server. + // If empty, the address of the transport should be used instead. + advertisedAddr string + unsafeTracker *unsafeHeadTracker } type RaftConsensusConfig struct { - ServerID string - ServerAddr string + ServerID string + + // AdvertisedAddr is the address to advertise, + // i.e. the address external raft peers use to contact us. + // If left empty, it defaults to the resulting + // local address that we bind the underlying transport to. + AdvertisedAddr raft.ServerAddress + + // ListenPort is the port to bind the server to. + // This may be 0, an available port will then be selected by the system. + ListenPort int + // ListenAddr is the address to bind the server to. + // E.g. use 0.0.0.0 to bind to an external-facing network. + ListenAddr string + StorageDir string Bootstrap bool RollupCfg *rollup.Config @@ -86,18 +104,31 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, return nil, fmt.Errorf(`raft.NewFileSnapshotStore(%q): %w`, baseDir, err) } - addr, err := net.ResolveTCPAddr("tcp", cfg.ServerAddr) - if err != nil { - return nil, errors.Wrap(err, "failed to resolve tcp address") + var advertiseAddr net.Addr + if cfg.AdvertisedAddr == "" { + log.Warn("No advertised address specified. Advertising local address.") + } else { + x, err := net.ResolveTCPAddr("tcp", string(cfg.AdvertisedAddr)) + if err != nil { + return nil, fmt.Errorf("failed to resolve advertised TCP address %q: %w", string(cfg.AdvertisedAddr), err) + } + advertiseAddr = x + log.Info("Resolved advertising address", "adAddr", cfg.AdvertisedAddr, + "adIP", x.IP, "adPort", x.Port, "adZone", x.Zone) } + bindAddr := fmt.Sprintf("%s:%d", cfg.ListenAddr, cfg.ListenPort) + log.Info("Binding raft server to network transport", "listenAddr", bindAddr) + maxConnPool := 10 timeout := 5 * time.Second - bindAddr := fmt.Sprintf("0.0.0.0:%d", addr.Port) - transport, err := raft.NewTCPTransportWithLogger(bindAddr, addr, maxConnPool, timeout, rc.Logger) + + // When advertiseAddr == nil, the transport will use the local address that it is bound to. + transport, err := raft.NewTCPTransportWithLogger(bindAddr, advertiseAddr, maxConnPool, timeout, rc.Logger) if err != nil { return nil, errors.Wrap(err, "failed to create raft tcp transport") } + log.Info("Raft server network transport is up", "addr", transport.LocalAddr()) fsm := NewUnsafeHeadTracker(log) @@ -110,11 +141,19 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, // If bootstrap = true, start raft in bootstrap mode, this will allow the current node to elect itself as leader when there's no other participants // and allow other nodes to join the cluster. if cfg.Bootstrap { + var advertisedAddr raft.ServerAddress + if cfg.AdvertisedAddr == "" { + advertisedAddr = transport.LocalAddr() + } else { + advertisedAddr = cfg.AdvertisedAddr + } + log.Info("Bootstrapping raft consensus cluster with self", "addr", advertisedAddr) + raftCfg := raft.Configuration{ Servers: []raft.Server{ { ID: rc.LocalID, - Address: raft.ServerAddress(cfg.ServerAddr), + Address: advertisedAddr, Suffrage: raft.Voter, }, }, @@ -132,9 +171,20 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, serverID: raft.ServerID(cfg.ServerID), unsafeTracker: fsm, rollupCfg: cfg.RollupCfg, + transport: transport, }, nil } +// Addr returns the address to contact this raft consensus server. +// If no explicit address to advertise was configured, +// the local network address that the raft-consensus server is listening on will be used. +func (rc *RaftConsensus) Addr() string { + if rc.advertisedAddr != "" { + return rc.advertisedAddr + } + return string(rc.transport.LocalAddr()) +} + // AddNonVoter implements Consensus, it tries to add a non-voting member into the cluster. func (rc *RaftConsensus) AddNonVoter(id string, addr string, version uint64) error { if err := checkTCPPortOpen(addr); err != nil { diff --git a/op-conductor/consensus/raft_test.go b/op-conductor/consensus/raft_test.go index fbd9c7cb3bc8..9c8ca48247ef 100644 --- a/op-conductor/consensus/raft_test.go +++ b/op-conductor/consensus/raft_test.go @@ -28,7 +28,9 @@ func TestCommitAndRead(t *testing.T) { } raftConsensusConfig := &RaftConsensusConfig{ ServerID: "SequencerA", - ServerAddr: "127.0.0.1:0", + ListenPort: 0, + ListenAddr: "127.0.0.1", // local test, don't bind to external interface + AdvertisedAddr: "", // use local address that the server binds to StorageDir: storageDir, Bootstrap: true, RollupCfg: rollupCfg, diff --git a/op-conductor/flags/flags.go b/op-conductor/flags/flags.go index 249e8a676e07..7c29bfcab2c3 100644 --- a/op-conductor/flags/flags.go +++ b/op-conductor/flags/flags.go @@ -19,16 +19,22 @@ const EnvVarPrefix = "OP_CONDUCTOR" var ( ConsensusAddr = &cli.StringFlag{ Name: "consensus.addr", - Usage: "Address to listen for consensus connections", + Usage: "Address (excluding port) to listen for consensus connections.", EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "CONSENSUS_ADDR"), Value: "127.0.0.1", } ConsensusPort = &cli.IntFlag{ Name: "consensus.port", - Usage: "Port to listen for consensus connections", + Usage: "Port to listen for consensus connections. May be 0 to let the system select a port.", EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "CONSENSUS_PORT"), Value: 50050, } + AdvertisedFullAddr = &cli.StringFlag{ + Name: "consensus.advertised", + Usage: "Full address (host and port) for other peers to contact the consensus server. Optional: if left empty, the local address is advertised.", + EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "CONSENSUS_ADVERTISED"), + Value: "", + } RaftBootstrap = &cli.BoolFlag{ Name: "raft.bootstrap", Usage: "If this node should bootstrap a new raft cluster", @@ -127,6 +133,7 @@ var requiredFlags = []cli.Flag{ } var optionalFlags = []cli.Flag{ + AdvertisedFullAddr, Paused, RPCEnableProxy, RaftBootstrap, diff --git a/op-e2e/system/conductor/sequencer_failover_setup.go b/op-e2e/system/conductor/sequencer_failover_setup.go index ea515ecc1e64..a4e5178e3d3e 100644 --- a/op-e2e/system/conductor/sequencer_failover_setup.go +++ b/op-e2e/system/conductor/sequencer_failover_setup.go @@ -2,9 +2,8 @@ package conductor import ( "context" + "errors" "fmt" - "math/rand" - "net" "strings" "testing" "time" @@ -51,28 +50,23 @@ const ( var retryStrategy = &retry.FixedStrategy{Dur: 50 * time.Millisecond} type conductor struct { - service *con.OpConductor - client conrpc.API - consensusPort int - rpcPort int + service *con.OpConductor + client conrpc.API } func (c *conductor) ConsensusEndpoint() string { - return fmt.Sprintf("%s:%d", localhost, c.consensusPort) + return c.service.ConsensusEndpoint() } func (c *conductor) RPCEndpoint() string { - return fmt.Sprintf("http://%s:%d", localhost, c.rpcPort) + return c.service.HTTPEndpoint() } func setupSequencerFailoverTest(t *testing.T) (*e2esys.System, map[string]*conductor, func()) { op_e2e.InitParallel(t) ctx := context.Background() - sys, conductors, err := retry.Do2(ctx, maxSetupRetries, retryStrategy, func() (*e2esys.System, map[string]*conductor, error) { - return setupHAInfra(t, ctx) - }) - require.NoError(t, err, "Expected to successfully setup sequencers and conductors after retry") + sys, conductors := setupHAInfra(t, ctx) // form a cluster c1 := conductors[Sequencer1Name] @@ -143,79 +137,80 @@ func setupSequencerFailoverTest(t *testing.T) (*e2esys.System, map[string]*condu } } -func setupHAInfra(t *testing.T, ctx context.Context) (*e2esys.System, map[string]*conductor, error) { +func setupHAInfra(t *testing.T, ctx context.Context) (*e2esys.System, map[string]*conductor) { startTime := time.Now() - - var sys *e2esys.System - var conductors map[string]*conductor - var err error - - // clean up if setup fails due to port in use. defer func() { - if err != nil { - if sys != nil { - sys.Close() - } - - for _, c := range conductors { - if c == nil || c.service == nil { - // pass. Sometimes we can get nil in this map - } else if serr := c.service.Stop(ctx); serr != nil { - t.Log("Failed to stop conductor", "error", serr) - } - } - } t.Logf("setupHAInfra took %s\n", time.Since(startTime)) }() - conductorRpcPorts := map[string]int{ - Sequencer1Name: findAvailablePort(t), - Sequencer2Name: findAvailablePort(t), - Sequencer3Name: findAvailablePort(t), + conductorsReady := map[string]chan string{ + Sequencer1Name: make(chan string, 1), + Sequencer2Name: make(chan string, 1), + Sequencer3Name: make(chan string, 1), } - // 3 sequencers, 1 verifier, 1 active sequencer. - cfg := sequencerFailoverSystemConfig(t, conductorRpcPorts) - if sys, err = cfg.Start(t); err != nil { - return nil, nil, err + // The sequencer op-node & execution engine have to be up first, to get their endpoints running. + // The conductor is then started after, using the endpoints of op-node and execution engine. + // The op-node, while starting, will wait for the conductor to be up and running, to get its endpoint. + // No endpoint is reserved/hardcoded this way, this avoids CI test flakes in the setup. + conductorEndpointFn := func(ctx context.Context, name string) (endpoint string, err error) { + endpointCh, ok := conductorsReady[name] + if !ok { + return "", errors.New("conductor %s is not known") + } + select { + case <-ctx.Done(): + return "", fmt.Errorf("failed to set up conductor timely: %w", err) + case endpoint := <-endpointCh: + return endpoint, nil + } } - // 3 conductors that connects to 1 sequencer each. - conductors = make(map[string]*conductor) + // 3 sequencers, 1 verifier, 1 active sequencer. + cfg := sequencerFailoverSystemConfig(t, conductorEndpointFn) + + // sys is configured to close itself on test cleanup. + sys, err := cfg.Start(t) + require.NoError(t, err, "must start system") + + out := make(map[string]*conductor) + // 3 conductors that connects to 1 sequencer each. // initialize all conductors in paused mode conductorCfgs := []struct { name string - port int bootstrap bool }{ - {Sequencer1Name, conductorRpcPorts[Sequencer1Name], true}, // one in bootstrap mode so that we can form a cluster. - {Sequencer2Name, conductorRpcPorts[Sequencer2Name], false}, - {Sequencer3Name, conductorRpcPorts[Sequencer3Name], false}, + {Sequencer1Name, true}, // one in bootstrap mode so that we can form a cluster. + {Sequencer2Name, false}, + {Sequencer3Name, false}, } for _, cfg := range conductorCfgs { cfg := cfg nodePRC := sys.RollupNodes[cfg.name].UserRPC().RPC() engineRPC := sys.EthInstances[cfg.name].UserRPC().RPC() - if conductors[cfg.name], err = setupConductor(t, cfg.name, t.TempDir(), nodePRC, engineRPC, cfg.port, cfg.bootstrap, *sys.RollupConfig); err != nil { - return nil, nil, err - } + + conduc, err := setupConductor(t, cfg.name, t.TempDir(), nodePRC, engineRPC, cfg.bootstrap, *sys.RollupConfig) + require.NoError(t, err, "failed to set up conductor %s", cfg.name) + out[cfg.name] = conduc + // Signal that the conductor RPC endpoint is ready + conductorsReady[cfg.name] <- conduc.RPCEndpoint() } - return sys, conductors, nil + return sys, out } func setupConductor( t *testing.T, serverID, dir, nodeRPC, engineRPC string, - rpcPort int, bootstrap bool, rollupCfg rollup.Config, ) (*conductor, error) { - consensusPort := findAvailablePort(t) cfg := con.Config{ - ConsensusAddr: localhost, - ConsensusPort: consensusPort, + ConsensusAddr: localhost, + ConsensusPort: 0, // let the system select a port, avoid conflicts + ConsensusAdvertisedAddr: "", // use the local address we bind to + RaftServerID: serverID, RaftStorageDir: dir, RaftBootstrap: bootstrap, @@ -237,17 +232,18 @@ func setupConductor( RollupCfg: rollupCfg, RPCEnableProxy: true, LogConfig: oplog.CLIConfig{ - Level: log.LevelInfo, + Level: log.LevelDebug, Color: false, }, RPC: oprpc.CLIConfig{ ListenAddr: localhost, - ListenPort: rpcPort, + ListenPort: 0, // let the system select a port }, } + logger := testlog.Logger(t, log.LevelDebug) ctx := context.Background() - service, err := con.New(ctx, &cfg, testlog.Logger(t, log.LevelInfo), "0.0.1") + service, err := con.New(ctx, &cfg, logger, "0.0.1") if err != nil { return nil, err } @@ -257,6 +253,8 @@ func setupConductor( return nil, err } + logger.Info("Started conductor", "nodeRPC", nodeRPC, "engineRPC", engineRPC) + rawClient, err := rpc.DialContext(ctx, service.HTTPEndpoint()) if err != nil { return nil, err @@ -265,10 +263,8 @@ func setupConductor( client := conrpc.NewAPIClient(rawClient) return &conductor{ - service: service, - client: client, - consensusPort: consensusPort, - rpcPort: rpcPort, + service: service, + client: client, }, nil } @@ -316,12 +312,18 @@ func setupBatcher(t *testing.T, sys *e2esys.System, conductors map[string]*condu sys.BatchSubmitter = batcher } -func sequencerFailoverSystemConfig(t *testing.T, ports map[string]int) e2esys.SystemConfig { +func sequencerFailoverSystemConfig(t *testing.T, conductorRPCEndpoints func(ctx context.Context, name string) (string, error)) e2esys.SystemConfig { cfg := e2esys.EcotoneSystemConfig(t, new(hexutil.Uint64)) delete(cfg.Nodes, "sequencer") - cfg.Nodes[Sequencer1Name] = sequencerCfg(ports[Sequencer1Name]) - cfg.Nodes[Sequencer2Name] = sequencerCfg(ports[Sequencer2Name]) - cfg.Nodes[Sequencer3Name] = sequencerCfg(ports[Sequencer3Name]) + cfg.Nodes[Sequencer1Name] = sequencerCfg(func(ctx context.Context) (string, error) { + return conductorRPCEndpoints(ctx, Sequencer1Name) + }) + cfg.Nodes[Sequencer2Name] = sequencerCfg(func(ctx context.Context) (string, error) { + return conductorRPCEndpoints(ctx, Sequencer2Name) + }) + cfg.Nodes[Sequencer3Name] = sequencerCfg(func(ctx context.Context) (string, error) { + return conductorRPCEndpoints(ctx, Sequencer3Name) + }) delete(cfg.Loggers, "sequencer") cfg.Loggers[Sequencer1Name] = testlog.Logger(t, log.LevelInfo).New("role", Sequencer1Name) @@ -338,7 +340,7 @@ func sequencerFailoverSystemConfig(t *testing.T, ports map[string]int) e2esys.Sy return cfg } -func sequencerCfg(rpcPort int) *rollupNode.Config { +func sequencerCfg(conductorRPCEndpoint rollupNode.ConductorRPCFunc) *rollupNode.Config { return &rollupNode.Config{ Driver: driver.Config{ VerifierConfDepth: 0, @@ -357,8 +359,8 @@ func sequencerCfg(rpcPort int) *rollupNode.Config { ConfigPersistence: &rollupNode.DisabledConfigPersistence{}, Sync: sync.Config{SyncMode: sync.CLSync}, ConductorEnabled: true, - ConductorRpc: fmt.Sprintf("http://%s:%d", localhost, rpcPort), - ConductorRpcTimeout: 1 * time.Second, + ConductorRpc: conductorRPCEndpoint, + ConductorRpcTimeout: 5 * time.Second, } } @@ -453,26 +455,6 @@ func sequencerActive(t *testing.T, ctx context.Context, rollupClient *sources.Ro return active } -func findAvailablePort(t *testing.T) int { - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - for { - select { - case <-ctx.Done(): - t.Error("Failed to find available port") - default: - // private / ephemeral ports are in the range 49152-65535 - port := rand.Intn(65535-49152) + 49152 - addr := fmt.Sprintf("127.0.0.1:%d", port) - l, err := net.Listen("tcp", addr) - if err == nil { - l.Close() // Close the listener and return the port if it's available - return port - } - } - } -} - func findLeader(t *testing.T, conductors map[string]*conductor) (string, *conductor) { for id, con := range conductors { if leader(t, context.Background(), con) { diff --git a/op-e2e/system/conductor/sequencer_failover_test.go b/op-e2e/system/conductor/sequencer_failover_test.go index 74047fcc6fdd..5722dc3b82e9 100644 --- a/op-e2e/system/conductor/sequencer_failover_test.go +++ b/op-e2e/system/conductor/sequencer_failover_test.go @@ -103,7 +103,6 @@ func TestSequencerFailover_ConductorRPC(t *testing.T) { t, VerifierName, t.TempDir(), sys.RollupEndpoint(Sequencer3Name).RPC(), sys.NodeEndpoint(Sequencer3Name).RPC(), - findAvailablePort(t), false, *sys.RollupConfig, ) diff --git a/op-node/node/conductor.go b/op-node/node/conductor.go index ff5723889b95..cc45f4bfabc4 100644 --- a/op-node/node/conductor.go +++ b/op-node/node/conductor.go @@ -13,15 +13,17 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/conductor" "github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/locks" "github.com/ethereum-optimism/optimism/op-service/retry" ) // ConductorClient is a client for the op-conductor RPC service. type ConductorClient struct { - cfg *Config - metrics *metrics.Metrics - log log.Logger - apiClient *conductorRpc.APIClient + cfg *Config + metrics *metrics.Metrics + log log.Logger + + apiClient locks.RWValue[*conductorRpc.APIClient] // overrideLeader is used to override the leader check for disaster recovery purposes. // During disaster situations where the cluster is unhealthy (no leader, only 1 or less nodes up), @@ -41,15 +43,23 @@ func NewConductorClient(cfg *Config, log log.Logger, metrics *metrics.Metrics) c } // Initialize initializes the conductor client. -func (c *ConductorClient) initialize() error { - if c.apiClient != nil { +func (c *ConductorClient) initialize(ctx context.Context) error { + c.apiClient.Lock() + defer c.apiClient.Unlock() + if c.apiClient.Value != nil { return nil } - conductorRpcClient, err := dial.DialRPCClientWithTimeout(context.Background(), time.Minute*1, c.log, c.cfg.ConductorRpc) + endpoint, err := retry.Do[string](ctx, 10, retry.Exponential(), func() (string, error) { + return c.cfg.ConductorRpc(ctx) + }) + if err != nil { + return fmt.Errorf("no conductor RPC endpoint available: %w", err) + } + conductorRpcClient, err := dial.DialRPCClientWithTimeout(context.Background(), time.Minute*1, c.log, endpoint) if err != nil { return fmt.Errorf("failed to dial conductor RPC: %w", err) } - c.apiClient = conductorRpc.NewAPIClient(conductorRpcClient) + c.apiClient.Value = conductorRpc.NewAPIClient(conductorRpcClient) return nil } @@ -64,7 +74,7 @@ func (c *ConductorClient) Leader(ctx context.Context) (bool, error) { return true, nil } - if err := c.initialize(); err != nil { + if err := c.initialize(ctx); err != nil { return false, err } ctx, cancel := context.WithTimeout(ctx, c.cfg.ConductorRpcTimeout) @@ -72,8 +82,11 @@ func (c *ConductorClient) Leader(ctx context.Context) (bool, error) { isLeader, err := retry.Do(ctx, 2, retry.Fixed(50*time.Millisecond), func() (bool, error) { record := c.metrics.RecordRPCClientRequest("conductor_leader") - result, err := c.apiClient.Leader(ctx) + result, err := c.apiClient.Get().Leader(ctx) record(err) + if err != nil { + c.log.Error("Failed to check conductor for leadership", "err", err) + } return result, err }) return isLeader, err @@ -85,7 +98,7 @@ func (c *ConductorClient) CommitUnsafePayload(ctx context.Context, payload *eth. return nil } - if err := c.initialize(); err != nil { + if err := c.initialize(ctx); err != nil { return err } ctx, cancel := context.WithTimeout(ctx, c.cfg.ConductorRpcTimeout) @@ -93,7 +106,7 @@ func (c *ConductorClient) CommitUnsafePayload(ctx context.Context, payload *eth. err := retry.Do0(ctx, 2, retry.Fixed(50*time.Millisecond), func() error { record := c.metrics.RecordRPCClientRequest("conductor_commitUnsafePayload") - err := c.apiClient.CommitUnsafePayload(ctx, payload) + err := c.apiClient.Get().CommitUnsafePayload(ctx, payload) record(err) return err }) @@ -107,9 +120,11 @@ func (c *ConductorClient) OverrideLeader(ctx context.Context) error { } func (c *ConductorClient) Close() { - if c.apiClient == nil { + c.apiClient.Lock() + defer c.apiClient.Unlock() + if c.apiClient.Value == nil { return } - c.apiClient.Close() - c.apiClient = nil + c.apiClient.Value.Close() + c.apiClient.Value = nil } diff --git a/op-node/node/config.go b/op-node/node/config.go index a78b55853aa2..6591a756e715 100644 --- a/op-node/node/config.go +++ b/op-node/node/config.go @@ -69,13 +69,16 @@ type Config struct { // Conductor is used to determine this node is the leader sequencer. ConductorEnabled bool - ConductorRpc string + ConductorRpc ConductorRPCFunc ConductorRpcTimeout time.Duration // AltDA config AltDA altda.CLIConfig } +// ConductorRPCFunc retrieves the endpoint. The RPC may not immediately be available. +type ConductorRPCFunc func(ctx context.Context) (string, error) + type RPCConfig struct { ListenAddr string ListenPort int diff --git a/op-node/node/node.go b/op-node/node/node.go index 298c98aa2b18..2727552b8190 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -445,6 +445,7 @@ func (n *OpNode) initRPCServer(cfg *Config) error { if err := server.Start(); err != nil { return fmt.Errorf("unable to start RPC server: %w", err) } + n.log.Info("Started JSON-RPC server", "addr", server.Addr()) n.server = server return nil } diff --git a/op-node/service.go b/op-node/service.go index b24e2a638335..4d12c7f5446f 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -1,6 +1,7 @@ package opnode import ( + "context" "crypto/rand" "encoding/json" "errors" @@ -80,7 +81,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ctx.IsSet(flags.HeartbeatURLFlag.Name) { log.Warn("Heartbeat functionality is not supported anymore, CLI flags will be removed in following release.") } - + conductorRPCEndpoint := ctx.String(flags.ConductorRpcFlag.Name) cfg := &node.Config{ L1: l1Endpoint, L2: l2Endpoint, @@ -108,8 +109,10 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { Sync: *syncConfig, RollupHalt: haltOption, - ConductorEnabled: ctx.Bool(flags.ConductorEnabledFlag.Name), - ConductorRpc: ctx.String(flags.ConductorRpcFlag.Name), + ConductorEnabled: ctx.Bool(flags.ConductorEnabledFlag.Name), + ConductorRpc: func(context.Context) (string, error) { + return conductorRPCEndpoint, nil + }, ConductorRpcTimeout: ctx.Duration(flags.ConductorRpcTimeoutFlag.Name), AltDA: altda.ReadCLIConfig(ctx), From ba74f8a8a6b50ff28744955651629af41aa93079 Mon Sep 17 00:00:00 2001 From: mbaxter Date: Fri, 8 Nov 2024 17:01:28 -0500 Subject: [PATCH 164/451] cannon: Port step post-checks to MIPS2.sol (#12815) --- .../snapshots/abi/MIPS2.json | 2 +- .../snapshots/semver-lock.json | 4 +- .../contracts-bedrock/src/cannon/MIPS2.sol | 38 +++++++++++++++++-- .../src/cannon/interfaces/IMIPS2.sol | 8 +++- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/abi/MIPS2.json b/packages/contracts-bedrock/snapshots/abi/MIPS2.json index 2294e23e754e..348868bfba0a 100644 --- a/packages/contracts-bedrock/snapshots/abi/MIPS2.json +++ b/packages/contracts-bedrock/snapshots/abi/MIPS2.json @@ -45,7 +45,7 @@ "outputs": [ { "internalType": "bytes32", - "name": "", + "name": "postState_", "type": "bytes32" } ], diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index cb4cc8a55be9..fb7b6d824392 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -144,8 +144,8 @@ "sourceCodeHash": "0xd8467700c80b3e62fa37193dc6513bac35282094b686b50e162e157f704dde00" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0xaedf0d0b0e94a0c5e7d987331d2fdba84230f5704a6ca33677e70cde7051b17e", - "sourceCodeHash": "0x9fa2d1297ad1e93b4d3c5c0fed08bedcd8f746807589f0fd3369e79347c6a027" + "initCodeHash": "0xc38c76ab3aad78c81ca01b3235b402614972d6604b22fda1e870f1bf47be1194", + "sourceCodeHash": "0x3d38b1924669d1bde756f1306601c764a6d31f428ac72667a3dd194b3388210d" }, "src/cannon/MIPS64.sol": { "initCodeHash": "0x93aa8d7f9fd3c22276c0d303a3fefdf8f73cc55807b35e483bba64c92d02aaef", diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 220c66ddde54..1d73473fde24 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.21 - string public constant version = "1.0.0-beta.21"; + /// @custom:semver 1.0.0-beta.22 + string public constant version = "1.0.0-beta.22"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -102,7 +102,39 @@ contract MIPS2 is ISemver { /// the current thread stack. /// @param _localContext The local key context for the preimage oracle. Optional, can be set as a constant /// if the caller only requires one set of local keys. - function step(bytes calldata _stateData, bytes calldata _proof, bytes32 _localContext) public returns (bytes32) { + /// @return postState_ The hash of the post state witness after the state transition. + function step( + bytes calldata _stateData, + bytes calldata _proof, + bytes32 _localContext + ) + public + returns (bytes32 postState_) + { + postState_ = doStep(_stateData, _proof, _localContext); + assertPostStateChecks(); + } + + function assertPostStateChecks() internal pure { + State memory state; + assembly { + state := STATE_MEM_OFFSET + } + + bytes32 activeStack = state.traverseRight ? state.rightThreadStack : state.leftThreadStack; + if (activeStack == EMPTY_THREAD_ROOT) { + revert("MIPS2: post-state active thread stack is empty"); + } + } + + function doStep( + bytes calldata _stateData, + bytes calldata _proof, + bytes32 _localContext + ) + internal + returns (bytes32) + { unchecked { State memory state; ThreadState memory thread; diff --git a/packages/contracts-bedrock/src/cannon/interfaces/IMIPS2.sol b/packages/contracts-bedrock/src/cannon/interfaces/IMIPS2.sol index 614daac1a731..be37d5b49ff7 100644 --- a/packages/contracts-bedrock/src/cannon/interfaces/IMIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/interfaces/IMIPS2.sol @@ -46,7 +46,13 @@ interface IMIPS2 is ISemver { error InvalidRMWInstruction(); function oracle() external view returns (IPreimageOracle oracle_); - function step(bytes memory _stateData, bytes memory _proof, bytes32 _localContext) external returns (bytes32); + function step( + bytes memory _stateData, + bytes memory _proof, + bytes32 _localContext + ) + external + returns (bytes32 postState_); function __constructor__(IPreimageOracle _oracle) external; } From a786daaf0f06eb0edf3c84a24049f9a32b9101f1 Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 11 Nov 2024 12:28:27 +0000 Subject: [PATCH 165/451] load HoloceneTime from registry (#12890) --- op-node/rollup/superchain.go | 1 + 1 file changed, 1 insertion(+) diff --git a/op-node/rollup/superchain.go b/op-node/rollup/superchain.go index 21a74323c05f..e4ca043011a2 100644 --- a/op-node/rollup/superchain.go +++ b/op-node/rollup/superchain.go @@ -91,6 +91,7 @@ func LoadOPStackRollupConfig(chainID uint64) (*Config, error) { EcotoneTime: chConfig.EcotoneTime, FjordTime: chConfig.FjordTime, GraniteTime: chConfig.GraniteTime, + HoloceneTime: chConfig.HoloceneTime, BatchInboxAddress: common.Address(chConfig.BatchInboxAddr), DepositContractAddress: common.Address(addrs.OptimismPortalProxy), L1SystemConfigAddress: common.Address(addrs.SystemConfigProxy), From 5af6860f69a3150733e5349d077efa8e9a6ddadc Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 11 Nov 2024 16:30:05 +0000 Subject: [PATCH 166/451] op-e2e/actions: improve Holocene tests by adding log assertions (#12889) * fix double error in inferring isHolocene * add capture logging to l2faultproofenv * fold log expectations into holoceneExpectations * improve test failure output * attach log expectations to invalid batch test case * add more log expectations * add more log expectations * add more test expectations for invalid payloads * add log assertion for deposits-only attributes * improve test names * improve subtest names * move holocene helpers to separate file * remove shadow * add log assertions for holocene_batches_test.go * remove assertion on logs in frame queue --- op-e2e/actions/proofs/helpers/env.go | 5 +- .../actions/proofs/holocene_batches_test.go | 47 +++++++++++------- op-e2e/actions/proofs/holocene_frame_test.go | 46 ++++++------------ .../actions/proofs/holocene_helpers_test.go | 45 +++++++++++++++++ .../proofs/holocene_invalid_batch_test.go | 48 ++++++++++++++----- 5 files changed, 128 insertions(+), 63 deletions(-) create mode 100644 op-e2e/actions/proofs/holocene_helpers_test.go diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 8ba29a6101ea..4226f4ccc4cf 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -28,6 +28,7 @@ import ( // L2FaultProofEnv is a test harness for a fault provable L2 chain. type L2FaultProofEnv struct { log log.Logger + Logs *testlog.CapturingHandler Batcher *helpers.L2Batcher Sequencer *helpers.L2Sequencer Engine *helpers.L2Engine @@ -40,7 +41,8 @@ type L2FaultProofEnv struct { } func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg) *L2FaultProofEnv { - log := testlog.Logger(t, log.LvlDebug) + log, logs := testlog.CaptureLogger(t, log.LevelDebug) + dp := NewDeployParams(t, tp, func(dp *e2eutils.DeployParams) { genesisBlock := hexutil.Uint64(0) @@ -111,6 +113,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut return &L2FaultProofEnv{ log: log, + Logs: logs, Batcher: batcher, Sequencer: sequencer, Engine: engine, diff --git a/op-e2e/actions/proofs/holocene_batches_test.go b/op-e2e/actions/proofs/holocene_batches_test.go index b67d2bd9da6d..8028aa0f5b90 100644 --- a/op-e2e/actions/proofs/holocene_batches_test.go +++ b/op-e2e/actions/proofs/holocene_batches_test.go @@ -25,41 +25,54 @@ func Test_ProgramAction_HoloceneBatches(gt *testing.T) { testCases := []testCase{ // Standard channel composition { - name: "case-0", blocks: []uint{1, 2, 3}, + name: "ordered", blocks: []uint{1, 2, 3}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, - safeHeadHolocene: 3, + preHolocene: expectations{safeHead: 3}, + holocene: expectations{safeHead: 3}, }, }, // Non-standard channel composition { - name: "case-2a", blocks: []uint{1, 3, 2}, + name: "disordered-a", blocks: []uint{1, 3, 2}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // batches are buffered, so the block ordering does not matter - safeHeadHolocene: 1, // batch for block 3 is considered invalid because it is from the future. This batch + remaining channel is dropped. + preHolocene: expectations{safeHead: 3}, // batches are buffered, so the block ordering does not matter + holocene: expectations{safeHead: 1, // batch for block 3 is considered invalid because it is from the future. This batch + remaining channel is dropped. + logs: append( + sequencerOnce("dropping future batch"), + sequencerOnce("Dropping invalid singular batch, flushing channel")..., + )}, }, }, { - name: "case-2b", blocks: []uint{2, 1, 3}, + name: "disordered-b", blocks: []uint{2, 1, 3}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // batches are buffered, so the block ordering does not matter - safeHeadHolocene: 0, // batch for block 2 is considered invalid because it is from the future. This batch + remaining channel is dropped. + preHolocene: expectations{safeHead: 3}, // batches are buffered, so the block ordering does not matter + holocene: expectations{safeHead: 0, // batch for block 2 is considered invalid because it is from the future. This batch + remaining channel is dropped. + logs: append( + sequencerOnce("dropping future batch"), + sequencerOnce("Dropping invalid singular batch, flushing channel")..., + )}, }, }, { - name: "case-2c", blocks: []uint{1, 1, 2, 3}, + name: "duplicates-a", blocks: []uint{1, 1, 2, 3}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // duplicate batches are silently dropped, so this reduceds to case-0 - safeHeadHolocene: 3, // duplicate batches are silently dropped + preHolocene: expectations{safeHead: 3}, // duplicate batches are dropped, so this reduces to the "ordered" case + holocene: expectations{safeHead: 3, // duplicate batches are dropped, so this reduces to the "ordered" case + logs: sequencerOnce("dropping past batch with old timestamp")}, }, }, { - name: "case-2d", blocks: []uint{2, 2, 1, 3}, + name: "duplicates-b", blocks: []uint{2, 2, 1, 3}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // duplicate batches are silently dropped, so this reduces to case-2b - safeHeadHolocene: 0, // duplicate batches are silently dropped, so this reduces to case-2b + preHolocene: expectations{safeHead: 3}, // duplicate batches are silently dropped, so this reduces to disordered-2b + holocene: expectations{safeHead: 0, // duplicate batches are silently dropped, so this reduces to disordered-2b + logs: append( + sequencerOnce("dropping future batch"), + sequencerOnce("Dropping invalid singular batch, flushing channel")..., + )}, }, }, } @@ -112,8 +125,8 @@ func Test_ProgramAction_HoloceneBatches(gt *testing.T) { env.Sequencer.ActL2PipelineFull(t) l2SafeHead := env.Sequencer.L2Safe() - testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) - + isHolocene := testCfg.Hardfork.Precedence >= helpers.Holocene.Precedence + testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) diff --git a/op-e2e/actions/proofs/holocene_frame_test.go b/op-e2e/actions/proofs/holocene_frame_test.go index 5093527b758d..2eb843d62ef8 100644 --- a/op-e2e/actions/proofs/holocene_frame_test.go +++ b/op-e2e/actions/proofs/holocene_frame_test.go @@ -7,28 +7,10 @@ import ( actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" "github.com/ethereum-optimism/optimism/op-program/client/claim" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) -type holoceneExpectations struct { - safeHeadPreHolocene uint64 - safeHeadHolocene uint64 -} - -func (h holoceneExpectations) RequireExpectedProgress(t actionsHelpers.StatefulTesting, actualSafeHead eth.L2BlockRef, isHolocene bool, engine *actionsHelpers.L2Engine) { - if isHolocene { - require.Equal(t, h.safeHeadPreHolocene, actualSafeHead.Number) - expectedHash := engine.L2Chain().GetBlockByNumber(h.safeHeadPreHolocene).Hash() - require.Equal(t, expectedHash, actualSafeHead.Hash) - } else { - require.Equal(t, h.safeHeadHolocene, actualSafeHead.Number) - expectedHash := engine.L2Chain().GetBlockByNumber(h.safeHeadHolocene).Hash() - require.Equal(t, expectedHash, actualSafeHead.Hash) - } -} - func Test_ProgramAction_HoloceneFrames(gt *testing.T) { type testCase struct { name string @@ -42,33 +24,33 @@ func Test_ProgramAction_HoloceneFrames(gt *testing.T) { testCases := []testCase{ // Standard frame submission, { - name: "case-0", frames: []uint{0, 1, 2}, + name: "ordered", frames: []uint{0, 1, 2}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, - safeHeadHolocene: 3, + preHolocene: expectations{safeHead: 3}, + holocene: expectations{safeHead: 3}, }, }, // Non-standard frame submission { - name: "case-1a", frames: []uint{2, 1, 0}, + name: "disordered-a", frames: []uint{2, 1, 0}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter - safeHeadHolocene: 0, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. + preHolocene: expectations{safeHead: 3}, // frames are buffered, so ordering does not matter + holocene: expectations{safeHead: 0}, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. }, }, { - name: "case-1b", frames: []uint{0, 1, 0, 2}, + name: "disordered-b", frames: []uint{0, 1, 0, 2}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter - safeHeadHolocene: 0, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. + preHolocene: expectations{safeHead: 3}, // frames are buffered, so ordering does not matter + holocene: expectations{safeHead: 0}, // non-first frames will be dropped b/c it is the first seen with that channel Id. The safe head won't move until the channel is closed/completed. }, }, { - name: "case-1c", frames: []uint{0, 1, 1, 2}, + name: "duplicates", frames: []uint{0, 1, 1, 2}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, // frames are buffered, so ordering does not matter - safeHeadHolocene: 3, // non-contiguous frames are dropped. So this reduces to case-0. + preHolocene: expectations{safeHead: 3}, // frames are buffered, so ordering does not matter + holocene: expectations{safeHead: 3}, // non-contiguous frames are dropped. So this reduces to case-0. }, }, } @@ -126,8 +108,8 @@ func Test_ProgramAction_HoloceneFrames(gt *testing.T) { l2SafeHead := env.Sequencer.L2Safe() - testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) - + isHolocene := testCfg.Hardfork.Precedence >= helpers.Holocene.Precedence + testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) diff --git a/op-e2e/actions/proofs/holocene_helpers_test.go b/op-e2e/actions/proofs/holocene_helpers_test.go new file mode 100644 index 000000000000..4b19185fe357 --- /dev/null +++ b/op-e2e/actions/proofs/holocene_helpers_test.go @@ -0,0 +1,45 @@ +package proofs + +import ( + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/stretchr/testify/require" +) + +type logExpectations struct { + role string + filter string + num int +} +type expectations struct { + safeHead uint64 + logs []logExpectations +} +type holoceneExpectations struct { + preHolocene, holocene expectations +} + +func (h holoceneExpectations) RequireExpectedProgressAndLogs(t actionsHelpers.StatefulTesting, actualSafeHead eth.L2BlockRef, isHolocene bool, engine *actionsHelpers.L2Engine, logs *testlog.CapturingHandler) { + var exp expectations + if isHolocene { + exp = h.holocene + } else { + exp = h.preHolocene + } + + require.Equal(t, exp.safeHead, actualSafeHead.Number) + expectedHash := engine.L2Chain().GetBlockByNumber(exp.safeHead).Hash() + require.Equal(t, expectedHash, actualSafeHead.Hash) + + for _, l := range exp.logs { + t.Helper() + recs := logs.FindLogs(testlog.NewMessageContainsFilter(l.filter), testlog.NewAttributesFilter("role", l.role)) + require.Len(t, recs, l.num, "searching for %d instances of '%s' in logs from role %s", l.num, l.filter, l.role) + } + +} + +func sequencerOnce(filter string) []logExpectations { + return []logExpectations{{filter: filter, role: "sequencer", num: 1}} +} diff --git a/op-e2e/actions/proofs/holocene_invalid_batch_test.go b/op-e2e/actions/proofs/holocene_invalid_batch_test.go index 3e210ab72ee4..202861401c9c 100644 --- a/op-e2e/actions/proofs/holocene_invalid_batch_test.go +++ b/op-e2e/actions/proofs/holocene_invalid_batch_test.go @@ -62,7 +62,7 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { { name: "valid", blocks: []uint{1, 2, 3}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 3, safeHeadHolocene: 3, + preHolocene: expectations{safeHead: 3}, holocene: expectations{safeHead: 3}, }, }, @@ -70,24 +70,38 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { name: "invalid-payload", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidPayload, nil}, useSpanBatch: false, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 1, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. - safeHeadHolocene: 2, // We expect the safe head to move to 2 due to creation of an deposit-only block. + preHolocene: expectations{safeHead: 1, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. + logs: sequencerOnce("could not process payload attributes"), + }, + holocene: expectations{safeHead: 2, // We expect the safe head to move to 2 due to creation of a deposit-only block. + logs: append( + sequencerOnce("Holocene active, requesting deposits-only attributes"), + sequencerOnce("could not process payload attributes")..., + ), + }, }, }, { name: "invalid-payload-span", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidPayload, nil}, useSpanBatch: true, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 0, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. - safeHeadHolocene: 2, // We expect the safe head to move to 2 due to creation of an deposit-only block. + preHolocene: expectations{safeHead: 0, // Invalid signature in block 2 causes an invalid _payload_ in the engine queue. Entire span batch is invalidated. + logs: sequencerOnce("could not process payload attributes"), + }, + + holocene: expectations{safeHead: 2, // We expect the safe head to move to 2 due to creation of an deposit-only block. + logs: sequencerOnce("could not process payload attributes"), + }, }, }, { name: "invalid-parent-hash", blocks: []uint{1, 2, 3}, blockModifiers: []actionsHelpers.BlockModifier{nil, invalidParentHash, nil}, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 1, // Invalid parentHash in block 2 causes an invalid batch to be dropped. - safeHeadHolocene: 1, // Same with Holocene. + preHolocene: expectations{safeHead: 1, // Invalid parentHash in block 2 causes an invalid batch to be dropped. + logs: sequencerOnce("ignoring batch with mismatching parent hash")}, + holocene: expectations{safeHead: 1, // Same with Holocene. + logs: sequencerOnce("Dropping invalid singular batch, flushing channel")}, }, }, { @@ -95,8 +109,12 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { useSpanBatch: true, breachMaxSequencerDrift: true, holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 0, // Entire span batch invalidated. - safeHeadHolocene: 1800, // We expect partial validity until we hit sequencer drift. + preHolocene: expectations{safeHead: 0, // Entire span batch invalidated. + logs: sequencerOnce("batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid"), + }, + holocene: expectations{safeHead: 1800, // We expect partial validity until we hit sequencer drift. + logs: sequencerOnce("batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid"), + }, }, }, { @@ -105,8 +123,12 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { useSpanBatch: true, overAdvanceL1Origin: 3, // this will over-advance the L1 origin of block 3 holoceneExpectations: holoceneExpectations{ - safeHeadPreHolocene: 0, // Entire span batch invalidated. - safeHeadHolocene: 2, // We expect partial validity, safe head should move to block 2, dropping invalid block 3 and remaining channel. + preHolocene: expectations{safeHead: 0, // Entire span batch invalidated. + logs: sequencerOnce("block timestamp is less than L1 origin timestamp"), + }, + holocene: expectations{safeHead: 2, // We expect partial validity, safe head should move to block 2, dropping invalid block 3 and remaining channel. + logs: sequencerOnce("batch timestamp is less than L1 origin timestamp"), + }, }, }, } @@ -204,8 +226,8 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { l2SafeHead := env.Sequencer.L2Safe() - testCfg.Custom.RequireExpectedProgress(t, l2SafeHead, testCfg.Hardfork.Precedence < helpers.Holocene.Precedence, env.Engine) - + isHolocene := testCfg.Hardfork.Precedence >= helpers.Holocene.Precedence + testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) if safeHeadNumber := l2SafeHead.Number; safeHeadNumber > 0 { From 9ae58ba02261ad96fbd11710dd6030ba643cceab Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 11 Nov 2024 21:31:29 +0000 Subject: [PATCH 167/451] op-e2e/actions: Cover Holocene activation and pipeline reset (#12891) * confirm holocene activation behavior TODO remove hack around setting holocene time TODO add log assertions * add holocene_activation_test.go * move holocene time hardcoding into test override * tidy * Update op-e2e/actions/proofs/holocene_activation_test.go Co-authored-by: clabby --------- Co-authored-by: clabby --- op-e2e/actions/proofs/helpers/env.go | 9 +- .../proofs/holocene_activation_test.go | 117 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 op-e2e/actions/proofs/holocene_activation_test.go diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 4226f4ccc4cf..7027647e142b 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -4,6 +4,7 @@ import ( "context" "math/rand" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" e2ecfg "github.com/ethereum-optimism/optimism/op-e2e/config" altda "github.com/ethereum-optimism/optimism/op-alt-da" @@ -40,7 +41,9 @@ type L2FaultProofEnv struct { Bob *helpers.CrossLayerUser } -func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg) *L2FaultProofEnv { +type deployConfigOverride func(*genesis.DeployConfig) + +func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg, deployConfigOverrides ...deployConfigOverride) *L2FaultProofEnv { log, logs := testlog.CaptureLogger(t, log.LevelDebug) dp := NewDeployParams(t, tp, func(dp *e2eutils.DeployParams) { @@ -66,6 +69,10 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut case Holocene: dp.DeployConfig.L2GenesisHoloceneTimeOffset = &genesisBlock } + + for _, override := range deployConfigOverrides { + override(dp.DeployConfig) + } }) sd := e2eutils.Setup(t, dp, helpers.DefaultAlloc) diff --git a/op-e2e/actions/proofs/holocene_activation_test.go b/op-e2e/actions/proofs/holocene_activation_test.go new file mode 100644 index 000000000000..55b8c1162de3 --- /dev/null +++ b/op-e2e/actions/proofs/holocene_activation_test.go @@ -0,0 +1,117 @@ +package proofs + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/require" +) + +func Test_ProgramAction_HoloceneActivation(gt *testing.T) { + + runHoloceneDerivationTest := func(gt *testing.T, testCfg *helpers.TestCfg[any]) { + t := actionsHelpers.NewDefaultTesting(gt) + + // Define override to activate Holocene 14 seconds after genesis + var setHoloceneTime = func(dc *genesis.DeployConfig) { + fourteen := hexutil.Uint64(14) + dc.L2GenesisHoloceneTimeOffset = &fourteen + } + + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), setHoloceneTime) + + t.Log("HoloceneTime: ", env.Sequencer.RollupCfg.HoloceneTime) + + // Build the L2 chain + blocks := []uint{1, 2} + targetHeadNumber := 2 + for env.Engine.L2Chain().CurrentBlock().Number.Uint64() < uint64(targetHeadNumber) { + env.Sequencer.ActL2StartBlock(t) + // Send an L2 tx + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTx(env.Alice.Address())(t) + env.Sequencer.ActL2EndBlock(t) + } + + // Build up a local list of frames + orderedFrames := make([][]byte, 0, 2) + // Buffer the blocks in the batcher and populate orderedFrames list + env.Batcher.ActCreateChannel(t, false) + for i, blockNum := range blocks { + env.Batcher.ActAddBlockByNumber(t, int64(blockNum), actionsHelpers.BlockLogger(t)) + if i == len(blocks)-1 { + env.Batcher.ActL2ChannelClose(t) + } + frame := env.Batcher.ReadNextOutputFrame(t) + require.NotEmpty(t, frame, "frame %d", i) + orderedFrames = append(orderedFrames, frame) + } + + includeBatchTx := func() { + // Include the last transaction submitted by the batcher. + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + } + + // Submit first frame + env.Batcher.ActL2BatchSubmitRaw(t, orderedFrames[0]) + includeBatchTx() // block should have a timestamp of 12s after genesis + + // Holocene should activate 14s after genesis, so that the previous l1 block + // was before HoloceneTime and the next l1 block is after it + + // Submit final frame + env.Batcher.ActL2BatchSubmitRaw(t, orderedFrames[1]) + includeBatchTx() // block should have a timestamp of 24s after genesis + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l2SafeHead := env.Sequencer.L2Safe() + require.EqualValues(t, uint64(0), l2SafeHead.Number) // channel should be dropped, so no safe head progression + t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) + + // Log assertions + filters := []string{ + "FrameQueue: resetting with Holocene activation", + "ChannelMux: transforming to Holocene stage", + "BatchMux: transforming to Holocene stage", + "dropping non-first frame without channel", + } + for _, filter := range filters { + recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter(filter), testlog.NewAttributesFilter("role", "sequencer")) + require.Len(t, recs, 1, "searching for %d instances of '%s' in logs from role %s", 1, filter, "sequencer") + } + + env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } + + matrix := helpers.NewMatrix[any]() + defer matrix.Run(gt) + + matrix.AddTestCase( + "HonestClaim-HoloceneActivation", + nil, + helpers.NewForkMatrix(helpers.Granite), + runHoloceneDerivationTest, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + "JunkClaim-HoloceneActivation", + nil, + helpers.NewForkMatrix(helpers.Granite), + runHoloceneDerivationTest, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) +} From 38ee0891bab0c5c6607354eb3479d8597b808f4f Mon Sep 17 00:00:00 2001 From: Inphi Date: Tue, 12 Nov 2024 00:29:38 -0800 Subject: [PATCH 168/451] cannon: Add more 64-bit tests (#12839) * cannon: Add more 64-bit tests * address review comments --- cannon/Makefile | 47 +- cannon/mipsevm/exec/mips_instructions.go | 4 +- cannon/mipsevm/tests/evm_common64_test.go | 243 ++------- cannon/mipsevm/tests/evm_common_test.go | 275 ++-------- .../mipsevm/tests/evm_multithreaded64_test.go | 122 +++++ .../mipsevm/tests/evm_multithreaded_test.go | 330 +++--------- cannon/mipsevm/tests/fuzz_evm_common_test.go | 97 +--- .../tests/fuzz_evm_multithreaded_test.go | 11 +- cannon/mipsevm/tests/testfuncs_test.go | 502 ++++++++++++++++++ cannon/mipsevm/testutil/arch.go | 8 + cannon/mipsevm/testutil/vmtests.go | 7 - 11 files changed, 820 insertions(+), 826 deletions(-) create mode 100644 cannon/mipsevm/tests/testfuncs_test.go diff --git a/cannon/Makefile b/cannon/Makefile index f71ffa24d200..6156cd68d64e 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -18,6 +18,9 @@ endif # The MIPS64 r1 opcodes not supported by cannon. This list does not include coprocess-specific opcodes. UNSUPPORTED_OPCODES := (dclo|dclz) +CANNON32_FUZZTIME := 10s +CANNON64_FUZZTIME := 20s + cannon32-impl: env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/cannon32-impl . @@ -87,28 +90,28 @@ cannon-stf-verify: fuzz: printf "%s\n" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT32 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallBrk64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallMmap64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallExitGroup64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallFcntl64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintRead64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintWrite64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite64 ./mipsevm/tests" \ - "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallCloneMT64 ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \ + "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \ | parallel -j 8 {} .PHONY: \ diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index bf1a8fdd0e7c..c8e27e63df01 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -455,10 +455,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem assertMips64(insn) return rt default: - panic("invalid instruction") + panic(fmt.Sprintf("invalid instruction: %x", insn)) } } - panic("invalid instruction") + panic(fmt.Sprintf("invalid instruction: %x", insn)) } func SignExtend(dat Word, idx Word) Word { diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go index d83ac4fa63d1..ce1253886742 100644 --- a/cannon/mipsevm/tests/evm_common64_test.go +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -8,22 +8,12 @@ import ( "os" "testing" - "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/stretchr/testify/require" ) -func TestEVMSingleStep_Operators64(t *testing.T) { - cases := []struct { - name string - isImm bool - rs Word - rt Word - imm uint16 - opcode uint32 - funct uint32 - expectRes Word - }{ +func TestEVM_SingleStep_Operators64(t *testing.T) { + cases := []operatorTestCase{ {name: "dadd. both unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // dadd t0, s1, s2 {name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 {name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 @@ -93,52 +83,26 @@ func TestEVMSingleStep_Operators64(t *testing.T) { {name: "dsrav max", funct: 0x17, rt: Word(0x7F_FF_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x0)}, {name: "dsrav max sign-extend", funct: 0x17, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FF)}, } + testOperators(t, cases, false) +} - v := GetMultiThreadedTestCase(t) - for i, tt := range cases { - testName := fmt.Sprintf("%v %v", v.Name, tt.name) - t.Run(testName, func(t *testing.T) { - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) - state := goVm.GetState() - var insn uint32 - var rsReg uint32 = 17 - var rtReg uint32 - var rdReg uint32 - if tt.isImm { - rtReg = 8 - insn = tt.opcode<<26 | rsReg<<21 | rtReg<<16 | uint32(tt.imm) - state.GetRegistersRef()[rtReg] = tt.rt - state.GetRegistersRef()[rsReg] = tt.rs - } else { - rtReg = 18 - rdReg = 8 - insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct - state.GetRegistersRef()[rsReg] = tt.rs - state.GetRegistersRef()[rtReg] = tt.rt - } - testutil.StoreInstruction(state.GetMemory(), 0, insn) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.ExpectStep() - if tt.isImm { - expected.Registers[rtReg] = tt.expectRes - } else { - expected.Registers[rdReg] = tt.expectRes - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) +func TestEVM_SingleStep_Bitwise64(t *testing.T) { + cases := []operatorTestCase{ + {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2 + {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40 + {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2 + {name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40 + {name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2 + {name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40 + {name: "nor", funct: 0x27, isImm: false, rs: Word(0x4b0), rt: Word(0x1ea), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FA_05)}, // nor t0, s1, s2 + {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(0)}, // slt t0, s1, s2 + {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FF_FF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2 + {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2 } + testOperators(t, cases, false) } -func TestEVMSingleStep_Shift64(t *testing.T) { +func TestEVM_SingleStep_Shift64(t *testing.T) { cases := []struct { name string rd Word @@ -218,17 +182,10 @@ func TestEVMSingleStep_Shift64(t *testing.T) { } } -func TestEVMSingleStep_LoadStore64(t *testing.T) { - cases := []struct { - name string - rs Word - rt Word - opcode uint32 - memVal Word - expectMemVal Word - expectRes Word - imm uint16 - }{ +func TestEVM_SingleStep_LoadStore64(t *testing.T) { + t1 := Word(0xFF000000_00000108) + + cases := []loadStoreTestCase{ {name: "lb 0", opcode: uint32(0x20), memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x71)}, // lb $t0, 0($t1) {name: "lb 1", opcode: uint32(0x20), imm: 1, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x72)}, // lb $t0, 1($t1) {name: "lb 2", opcode: uint32(0x20), imm: 2, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x73)}, // lb $t0, 2($t1) @@ -421,63 +378,15 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) { {name: "sd", opcode: uint32(0x3f), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sd $t0, 0($t1) {name: "sd signed", opcode: uint32(0x3f), rt: Word(0x81_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x81_22_33_44_55_66_77_88)}, // sd $t0, 4($t1) } - - v := GetMultiThreadedTestCase(t) - var t1 Word = 0xFF000000_00000108 - var baseReg uint32 = 9 - var rtReg uint32 = 8 - for i, tt := range cases { - testName := fmt.Sprintf("%v %v", v.Name, tt.name) - t.Run(testName, func(t *testing.T) { - effAddr := arch.AddressMask & t1 - - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) - state := goVm.GetState() - - insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) - state.GetRegistersRef()[rtReg] = tt.rt - state.GetRegistersRef()[baseReg] = t1 - - testutil.StoreInstruction(state.GetMemory(), 0, insn) - state.GetMemory().SetWord(t1&arch.AddressMask, tt.memVal) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.ExpectStep() - if tt.expectMemVal != 0 { - expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal) - } else { - expected.Registers[rtReg] = tt.expectRes - } - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) + // use a fixed base for all tests + for i := range cases { + cases[i].base = t1 } + testLoadStore(t, cases) } -func TestEVMSingleStep_DivMult64(t *testing.T) { - cases := []struct { - name string - rs Word - rt Word - funct uint32 - expectLo Word - expectHi Word - expectPanic string - }{ - // TODO(#12598): Fix 32-bit tests and remove these - {name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, - {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0x0), expectLo: Word(0x1)}, - {name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), expectHi: Word(0xE), expectLo: Word(0xFF_FF_FF_FF_FC_FC_FD_27)}, - {name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)}, - {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0xFF_FF_FF_FF_FF_FF_FF_FE), expectLo: Word(0x1)}, - {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_BE), expectHi: Word(0xFF_FF_FF_FF_AA_BB_CC_9F), expectLo: Word(0xFF_FF_FF_FF_FC_FD_02_9A)}, - +func TestEVM_SingleStep_MulDiv64(t *testing.T) { + cases := []mulDivTestCase{ // dmult s1, s2 // expected hi,lo were verified using qemu-mips {name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, @@ -514,9 +423,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { {name: "dmultu 14", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x8F_FF_FF_FF_FF_FF_FF_FF), expectLo: 0xF0_00_00_00_00_00_00_01, expectHi: 0x47_FF_FF_FF_FF_FF_FF_FE}, // ddiv rs, rt - {name: "ddiv", funct: 0x1e, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, - {name: "ddiv", funct: 0x1e, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, - {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, + {name: "ddiv", funct: 0x1e, rs: 1, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, {name: "ddiv", funct: 0x1e, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, {name: "ddiv", funct: 0x1e, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, {name: "ddiv", funct: 0x1e, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, @@ -527,9 +436,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { {name: "ddiv", funct: 0x1e, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0x80_00_00_01_00_00_00_00, expectHi: 0}, // ddivu - {name: "ddivu", funct: 0x1f, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, - {name: "ddivu", funct: 0x1f, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, - {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, + {name: "ddivu", funct: 0x1f, rs: 1, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, {name: "ddivu", funct: 0x1f, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, {name: "ddivu", funct: 0x1f, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, {name: "ddivu", funct: 0x1f, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, @@ -540,57 +449,16 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { {name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0, expectHi: 0x7F_FF_FF_FF_00_00_00_00}, // a couple div/divu 64-bit edge cases - {name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"}, - {name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"}, + {name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, + {name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, } - v := GetMultiThreadedTestCase(t) - for i, tt := range cases { - testName := fmt.Sprintf("%v %v", v.Name, tt.name) - t.Run(testName, func(t *testing.T) { - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) - state := goVm.GetState() - var rsReg uint32 = 17 - var rtReg uint32 = 18 - insn := rsReg<<21 | rtReg<<16 | tt.funct - state.GetRegistersRef()[rsReg] = tt.rs - state.GetRegistersRef()[rtReg] = tt.rt - testutil.StoreInstruction(state.GetMemory(), 0, insn) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.ExpectStep() - expected.LO = tt.expectLo - expected.HI = tt.expectHi - - if tt.expectPanic != "" { - require.PanicsWithValue(t, tt.expectPanic, func() { _, _ = goVm.Step(true) }) - // TODO(#12250): Assert EVM panic for divide by zero - // testutil.AssertEVMReverts(t, state, contracts, nil, proofData, errMsg) - } else { - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - } - }) - } + testMulDiv(t, cases, false) } -func TestEVMSingleStep_Branch64(t *testing.T) { - versions := GetMipsVersionTestCases(t) - cases := []struct { - name string - pc Word - expectNextPC Word - opcode uint32 - regimm uint32 - expectLink bool - rs arch.SignedInteger - rt Word - offset uint16 - }{ +func TestEVM_SingleStep_Branch64(t *testing.T) { + t.Parallel() + cases := []branchTestCase{ // blez {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, @@ -635,34 +503,5 @@ func TestEVMSingleStep_Branch64(t *testing.T) { {name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true}, } - for _, v := range versions { - for i, tt := range cases { - testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) - t.Run(testName, func(t *testing.T) { - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) - state := goVm.GetState() - const rsReg = 8 // t0 - insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) - testutil.StoreInstruction(state.GetMemory(), tt.pc, insn) - state.GetRegistersRef()[rsReg] = Word(tt.rs) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.Step += 1 - expected.PC = state.GetCpu().NextPC - expected.NextPC = tt.expectNextPC - if tt.expectLink { - expected.Registers[31] = state.GetPC() + 8 - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) - } - } + testBranch(t, cases) } diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index f61ee0850d51..6845fb07d59a 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -20,7 +20,9 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func TestEVM(t *testing.T) { +func TestEVM_OpenMIPS(t *testing.T) { + testutil.Cannon32OnlyTest(t, "Skipping MIPS32 assembly test vectors for cannon64") + testFiles, err := os.ReadDir("open_mips_tests/test/bin") require.NoError(t, err) @@ -36,7 +38,6 @@ func TestEVM(t *testing.T) { for _, f := range testFiles { testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name) t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) for _, skipped := range skipped { if f.Name() == skipped { t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name()) @@ -105,7 +106,7 @@ func TestEVM(t *testing.T) { } } -func TestEVMSingleStep_Jump(t *testing.T) { +func TestEVM_SingleStep_Jump(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -150,18 +151,8 @@ func TestEVMSingleStep_Jump(t *testing.T) { } } -func TestEVMSingleStep_Operators(t *testing.T) { - versions := GetMipsVersionTestCases(t) - cases := []struct { - name string - isImm bool - rs Word - rt Word - imm uint16 - funct uint32 - opcode uint32 - expectRes Word - }{ +func TestEVM_SingleStep_Operators(t *testing.T) { + cases := []operatorTestCase{ {name: "add", funct: 0x20, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // add t0, s1, s2 {name: "add", funct: 0x20, isImm: false, rs: ^Word(0), rt: ^Word(0), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2 {name: "add", funct: 0x20, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(0x7F_FF_FF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2 @@ -191,7 +182,14 @@ func TestEVMSingleStep_Operators(t *testing.T) { {name: "subu", funct: 0x23, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // subu t0, s1, s2 {name: "subu", funct: 0x23, isImm: false, rs: ^Word(0), rt: Word(1), expectRes: Word(0xFF_FF_FF_FE)}, // subu t0, s1, s2 {name: "subu", funct: 0x23, isImm: false, rs: Word(1), rt: ^Word(0), expectRes: Word(0x2)}, // subu t0, s1, s2 + } + testOperators(t, cases, true) +} +func TestEVM_SingleStep_Bitwise32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_Bitwise64") + // bitwise operations that use the full word size + cases := []operatorTestCase{ {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2 {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40 {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2 @@ -202,70 +200,15 @@ func TestEVMSingleStep_Operators(t *testing.T) { {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2 {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2 } - - for _, v := range versions { - for i, tt := range cases { - testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) - t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) - state := goVm.GetState() - var insn uint32 - var baseReg uint32 = 17 - var rtReg uint32 - var rdReg uint32 - if tt.isImm { - rtReg = 8 - insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) - state.GetRegistersRef()[rtReg] = tt.rt - state.GetRegistersRef()[baseReg] = tt.rs - } else { - rtReg = 18 - rdReg = 8 - insn = baseReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct - state.GetRegistersRef()[baseReg] = tt.rs - state.GetRegistersRef()[rtReg] = tt.rt - } - testutil.StoreInstruction(state.GetMemory(), 0, insn) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.Step += 1 - expected.PC = 4 - expected.NextPC = 8 - if tt.isImm { - expected.Registers[rtReg] = tt.expectRes - } else { - expected.Registers[rdReg] = tt.expectRes - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) - } - } + testOperators(t, cases, false) } -func TestEVMSingleStep_LoadStore(t *testing.T) { +func TestEVM_SingleStep_LoadStore32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_LoadStore64") loadMemVal := Word(0x11_22_33_44) loadMemValNeg := Word(0xF1_F2_F3_F4) rtVal := Word(0xaa_bb_cc_dd) - versions := GetMipsVersionTestCases(t) - cases := []struct { - name string - rt Word - base Word - imm uint32 - opcode uint32 - memVal Word - expectMemVal Word - expectRes Word - }{ + cases := []loadStoreTestCase{ {name: "lb, offset=0", opcode: uint32(0x20), base: 0x100, imm: 0x20, memVal: loadMemVal, expectRes: 0x11}, {name: "lb, offset=1", opcode: uint32(0x20), base: 0x100, imm: 0x1, memVal: loadMemVal, expectRes: 0x22}, {name: "lb, offset=2", opcode: uint32(0x20), base: 0x100, imm: 0x2, memVal: loadMemVal, expectRes: 0x33}, @@ -302,49 +245,10 @@ func TestEVMSingleStep_LoadStore(t *testing.T) { {name: "sw", opcode: uint32(0x2b), base: 0x100, imm: 0x20, rt: rtVal, expectMemVal: 0xaa_bb_cc_dd}, {name: "swr unaligned", opcode: uint32(0x2e), base: 0x100, imm: 0x1, rt: rtVal, expectMemVal: 0xcc_dd_00_00}, } - - var baseReg uint32 = 9 - var rtReg uint32 = 8 - for i, tt := range cases { - for _, v := range versions { - testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) - t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - addr := tt.base + Word(tt.imm) - effAddr := arch.AddressMask & addr - - // Setup - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) - state := goVm.GetState() - insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.imm - state.GetRegistersRef()[rtReg] = tt.rt - state.GetRegistersRef()[baseReg] = tt.base - testutil.StoreInstruction(state.GetMemory(), 0, insn) - state.GetMemory().SetWord(effAddr, tt.memVal) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.ExpectStep() - if tt.expectMemVal != 0 { - expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal) - } else { - expected.Registers[rtReg] = tt.expectRes - } - - // Run vm - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Validate - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) - } - } + testLoadStore(t, cases) } -func TestEVMSingleStep_MovzMovn(t *testing.T) { +func TestEVM_SingleStep_MovzMovn(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -406,7 +310,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { } -func TestEVMSingleStep_MfhiMflo(t *testing.T) { +func TestEVM_SingleStep_MfhiMflo(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -442,23 +346,8 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) { } } -func TestEVMSingleStep_MulDiv(t *testing.T) { - var tracer *tracing.Hooks - - versions := GetMipsVersionTestCases(t) - cases := []struct { - name string - rs Word - rt Word - funct uint32 - opcode uint32 - expectHi Word - expectLo Word - expectRes Word - rdReg uint32 - expectRevert string - errMsg string - }{ +func TestEVM_SingleStep_MulDiv(t *testing.T) { + cases := []mulDivTestCase{ {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(5), rt: Word(2), rdReg: uint32(0x8), expectRes: Word(10)}, // mul t0, t1, t2 {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0x1), rt: ^Word(0), rdReg: uint32(0x8), expectRes: ^Word(0)}, // mul t1, t2 {name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x8), expectRes: Word(0x1)}, // mul t1, t2 @@ -473,62 +362,18 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { {name: "multu", funct: uint32(0x19), rs: Word(0x1), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x0), expectLo: Word(0xFF_FF_FF_FF)}, // multu t1, t2 {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xFF_FF_FF_FE), expectLo: Word(0x1)}, // multu t1, t2 {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xAA_BB_CC_BE), expectLo: Word(0xFC_FC_FD_27)}, // multu t1, t2 + {name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_BE), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xAA_BB_CC_9F), expectLo: Word(0xFC_FD_02_9A)}, // multu t1, t2 - {name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2 - {name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // div t1, t2 - {name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2 - {name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // divu t1, t2 + {name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2 + {name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, // div t1, t2 + {name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2 + {name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, // divu t1, t2 } - for _, v := range versions { - for i, tt := range cases { - testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) - t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) - state := goVm.GetState() - var insn uint32 - baseReg := uint32(0x9) - rtReg := uint32(0xa) - - insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.rdReg<<11 | tt.funct - state.GetRegistersRef()[rtReg] = tt.rt - state.GetRegistersRef()[baseReg] = tt.rs - testutil.StoreInstruction(state.GetMemory(), 0, insn) - - if tt.expectRevert != "" { - proofData := v.ProofGenerator(t, goVm.GetState()) - require.PanicsWithValue(t, tt.expectRevert, func() { - _, _ = goVm.Step( - false) - }) - testutil.AssertEVMReverts(t, state, v.Contracts, tracer, proofData, testutil.CreateErrorStringMatcher(tt.errMsg)) - return - } - - step := state.GetStep() - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.ExpectStep() - if tt.expectRes != 0 { - expected.Registers[tt.rdReg] = tt.expectRes - } else { - expected.HI = tt.expectHi - expected.LO = tt.expectLo - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) - } - } + testMulDiv(t, cases, true) } -func TestEVMSingleStep_MthiMtlo(t *testing.T) { +func TestEVM_SingleStep_MthiMtlo(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -631,7 +476,7 @@ func TestEVM_MMap(t *testing.T) { } } -func TestEVMSysWriteHint(t *testing.T) { +func TestEVM_SysWriteHint(t *testing.T) { versions := GetMipsVersionTestCases(t) cases := []struct { name string @@ -824,7 +669,7 @@ func TestEVMSysWriteHint(t *testing.T) { } } -func TestEVMFault(t *testing.T) { +func TestEVM_Fault(t *testing.T) { var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer versions := GetMipsVersionTestCases(t) @@ -846,7 +691,7 @@ func TestEVMFault(t *testing.T) { errMsg testutil.ErrMatcher memoryProofAddresses []Word }{ - {name: "illegal instruction", nextPC: 0, insn: 0xFF_FF_FF_FF, errMsg: testutil.CreateErrorStringMatcher("invalid instruction"), memoryProofAddresses: []Word{0xa7ef00cc}}, + {name: "illegal instruction", nextPC: 0, insn: 0b111110 << 26, errMsg: testutil.CreateErrorStringMatcher("invalid instruction"), memoryProofAddresses: []Word{0x0}}, // memoryProof for the zero address at register 0 (+ imm) {name: "branch in delay-slot", nextPC: 8, insn: 0x11_02_00_03, errMsg: testutil.CreateErrorStringMatcher("branch in delay slot")}, {name: "jump in delay-slot", nextPC: 8, insn: 0x0c_00_00_0c, errMsg: testutil.CreateErrorStringMatcher("jump in delay slot")}, {name: "misaligned instruction", pc: 1, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()}, @@ -859,7 +704,6 @@ func TestEVMFault(t *testing.T) { for _, tt := range cases { testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC)) state := goVm.GetState() testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) @@ -874,7 +718,7 @@ func TestEVMFault(t *testing.T) { } } -func TestHelloEVM(t *testing.T) { +func TestEVM_HelloProgram(t *testing.T) { if os.Getenv("SKIP_SLOW_TESTS") == "true" { t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") } @@ -921,7 +765,7 @@ func TestHelloEVM(t *testing.T) { } } -func TestClaimEVM(t *testing.T) { +func TestEVM_ClaimProgram(t *testing.T) { if os.Getenv("SKIP_SLOW_TESTS") == "true" { t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") } @@ -966,7 +810,7 @@ func TestClaimEVM(t *testing.T) { } } -func TestEntryEVM(t *testing.T) { +func TestEVM_EntryProgram(t *testing.T) { if os.Getenv("SKIP_SLOW_TESTS") == "true" { t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") } @@ -1010,19 +854,10 @@ func TestEntryEVM(t *testing.T) { } } -func TestEVMSingleStepBranch(t *testing.T) { - versions := GetMipsVersionTestCases(t) - cases := []struct { - name string - pc Word - expectNextPC Word - opcode uint32 - regimm uint32 - expectLink bool - rs arch.SignedInteger - rt Word - offset uint16 - }{ +func TestEVM_SingleStep_Branch32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_Branch64") + t.Parallel() + cases := []branchTestCase{ // blez {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, @@ -1076,35 +911,5 @@ func TestEVMSingleStepBranch(t *testing.T) { {name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true}, } - for _, v := range versions { - for i, tt := range cases { - testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) - t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) - state := goVm.GetState() - const rsReg = 8 // t0 - insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) - testutil.StoreInstruction(state.GetMemory(), tt.pc, insn) - state.GetRegistersRef()[rsReg] = Word(tt.rs) - step := state.GetStep() - - // Setup expectations - expected := testutil.NewExpectedState(state) - expected.Step += 1 - expected.PC = state.GetCpu().NextPC - expected.NextPC = tt.expectNextPC - if tt.expectLink { - expected.Registers[31] = state.GetPC() + 8 - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) - }) - } - } + testBranch(t, cases) } diff --git a/cannon/mipsevm/tests/evm_multithreaded64_test.go b/cannon/mipsevm/tests/evm_multithreaded64_test.go index 5c5963a0e027..911a0d256a3c 100644 --- a/cannon/mipsevm/tests/evm_multithreaded64_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded64_test.go @@ -5,10 +5,13 @@ package tests import ( + "encoding/binary" "fmt" + "slices" "testing" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" @@ -360,3 +363,122 @@ func TestEVM_MT64_SCD(t *testing.T) { } } } + +func TestEVM_MT_SysRead_Preimage64(t *testing.T) { + preimageValue := make([]byte, 0, 8) + preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78) + preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32) + prestateMem := Word(0xEE_EE_EE_EE_FF_FF_FF_FF) + cases := []testMTSysReadPreimageTestCase{ + {name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_EE_EE_EE_FF_FF_FF_FF}, + {name: "Aligned addr, write 2 byte", addr: 0x00_00_FF_00, count: 2, writeLen: 2, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_EE_EE_FF_FF_FF_FF}, + {name: "Aligned addr, write 3 byte", addr: 0x00_00_FF_00, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_EE_FF_FF_FF_FF}, + {name: "Aligned addr, write 4 byte", addr: 0x00_00_FF_00, count: 4, writeLen: 4, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_FF_FF_FF_FF}, + {name: "Aligned addr, write 5 byte", addr: 0x00_00_FF_00, count: 5, writeLen: 5, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_FF_FF_FF}, + {name: "Aligned addr, write 6 byte", addr: 0x00_00_FF_00, count: 6, writeLen: 6, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_FF_FF}, + {name: "Aligned addr, write 7 byte", addr: 0x00_00_FF_00, count: 7, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_FF}, + {name: "Aligned addr, write 8 byte", addr: 0x00_00_FF_00, count: 8, writeLen: 8, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_32}, + + {name: "1-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_01, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_EE_EE_FF_FF_FF_FF}, + {name: "1-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_01, count: 2, writeLen: 2, preimageOffset: 9, prestateMem: prestateMem, postateMem: 0xEE_34_56_EE_FF_FF_FF_FF}, + {name: "1-byte misaligned addr, write 3 byte", addr: 0x00_00_FF_01, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_FF_FF_FF_FF}, + {name: "1-byte misaligned addr, write 4 byte", addr: 0x00_00_FF_01, count: 4, writeLen: 4, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_FF_FF_FF}, + {name: "1-byte misaligned addr, write 5 byte", addr: 0x00_00_FF_01, count: 5, writeLen: 5, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_FF_FF}, + {name: "1-byte misaligned addr, write 6 byte", addr: 0x00_00_FF_01, count: 6, writeLen: 6, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_FF}, + {name: "1-byte misaligned addr, write 7 byte", addr: 0x00_00_FF_01, count: 7, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_54}, + + {name: "2-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_02, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_12_EE_FF_FF_FF_FF}, + {name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 2, writeLen: 2, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_FF_FF_FF_FF}, + {name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 3, writeLen: 3, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_54_FF_FF_FF}, + {name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 4, writeLen: 4, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_54_32_FF_FF}, + + {name: "3-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_03, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_12_FF_FF_FF_FF}, + {name: "4-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_04, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_12_FF_FF_FF}, + {name: "5-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_05, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_12_FF_FF}, + {name: "6-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_06, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_12_FF}, + {name: "7-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_07, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_12}, + + {name: "Count of 0", addr: 0x00_00_FF_03, count: 0, writeLen: 0, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF}, + {name: "Count greater than 8", addr: 0x00_00_FF_00, count: 15, writeLen: 8, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_32}, + {name: "Count greater than 8, unaligned", addr: 0x00_00_FF_01, count: 15, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_54}, + {name: "Offset at last byte", addr: 0x00_00_FF_00, count: 8, writeLen: 1, preimageOffset: 15, prestateMem: prestateMem, postateMem: 0x32_EE_EE_EE_FF_FF_FF_FF}, + {name: "Offset just out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 16, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF, shouldPanic: true}, + {name: "Offset out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 17, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF, shouldPanic: true}, + } + testMTSysReadPreimage(t, preimageValue, cases) +} + +func TestEVM_MT_StoreOpsClearMemReservation64(t *testing.T) { + t.Parallel() + cases := []testMTStoreOpsClearMemReservationTestCase{ + {name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x78_FF_FF_FF_FF_FF_FF_FF}, + {name: "Store byte lower", opcode: 0b10_1000, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_78_FF_FF_FF}, + {name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x56_78_FF_FF_FF_FF_FF_FF}, + {name: "Store halfword lower", opcode: 0b10_1001, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_56_78_FF_FF}, + {name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x12_34_56_78_FF_FF_FF_FF}, + {name: "Store word left lower", opcode: 0b10_1010, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_12_34_56_78}, + {name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x12_34_56_78_FF_FF_FF_FF}, + {name: "Store word lower", opcode: 0b10_1011, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_12_34_56_78}, + {name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x78_FF_FF_FF_FF_FF_FF_FF}, + {name: "Store word right lower", opcode: 0b10_1110, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_78_FF_FF_FF}, + } + testMTStoreOpsClearMemReservation(t, cases) +} + +var NoopSyscalls64 = map[string]uint32{ + "SysMunmap": 5011, + "SysGetAffinity": 5196, + "SysMadvise": 5027, + "SysRtSigprocmask": 5014, + "SysSigaltstack": 5129, + "SysRtSigaction": 5013, + "SysPrlimit64": 5297, + "SysClose": 5003, + "SysPread64": 5016, + "SysStat": 5004, + "SysFstat": 5005, + //"SysFstat64": UndefinedSysNr, + "SysOpenAt": 5247, + "SysReadlink": 5087, + "SysReadlinkAt": 5257, + "SysIoctl": 5015, + "SysEpollCreate1": 5285, + "SysPipe2": 5287, + "SysEpollCtl": 5208, + "SysEpollPwait": 5272, + "SysGetRandom": 5313, + "SysUname": 5061, + //"SysStat64": UndefinedSysNr, + "SysGetuid": 5100, + "SysGetgid": 5102, + //"SysLlseek": UndefinedSysNr, + "SysMinCore": 5026, + "SysTgkill": 5225, + "SysGetRLimit": 5095, + "SysLseek": 5008, + "SysSetITimer": 5036, + "SysTimerCreate": 5216, + "SysTimerSetTime": 5217, + "SysTimerDelete": 5220, +} + +func TestEVM_NoopSyscall64(t *testing.T) { + testNoopSyscall(t, NoopSyscalls64) +} + +func TestEVM_UnsupportedSyscall64(t *testing.T) { + t.Parallel() + + var noopSyscallNums = maps.Values(NoopSyscalls64) + var SupportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid} + unsupportedSyscalls := make([]uint32, 0, 400) + for i := 5000; i < 5400; i++ { + candidate := uint32(i) + if slices.Contains(SupportedSyscalls, candidate) || slices.Contains(noopSyscallNums, candidate) { + continue + } + unsupportedSyscalls = append(unsupportedSyscalls, candidate) + } + + testUnsupportedSyscall(t, unsupportedSyscalls) +} diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index ba858585980b..1353932dd71f 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" @@ -19,7 +18,6 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" - preimage "github.com/ethereum-optimism/optimism/op-preimage" ) type Word = arch.Word @@ -190,37 +188,14 @@ func TestEVM_MT_SC(t *testing.T) { } } -func TestEVM_MT_SysRead_Preimage(t *testing.T) { +func TestEVM_MT_SysRead_Preimage32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_MT_SysRead_Preimage64") + + t.Parallel() preimageValue := make([]byte, 0, 8) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32) - - llVariations := []struct { - name string - llReservationStatus multithreaded.LLReservationStatus - matchThreadId bool - effAddrOffset Word - shouldClearReservation bool - }{ - {name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true}, - {name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true}, - {name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true}, - {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, - {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false}, - {name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true}, - {name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, - } - - cases := []struct { - name string - addr Word - count Word - writeLen Word - preimageOffset Word - prestateMem Word - postateMem Word - shouldPanic bool - }{ + cases := []testMTSysReadPreimageTestCase{ {name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_FF_FF_FF}, {name: "Aligned addr, write 2 byte", addr: 0x00_00_FF_00, count: 2, writeLen: 2, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_FF_FF}, {name: "Aligned addr, write 3 byte", addr: 0x00_00_FF_00, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_56_FF}, @@ -238,147 +213,22 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { {name: "Offset just out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 16, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true}, {name: "Offset out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 17, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true}, } - for i, c := range cases { - for _, v := range llVariations { - tName := fmt.Sprintf("%v (%v)", c.name, v.name) - t.Run(tName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - effAddr := arch.AddressMask & c.addr - preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() - oracle := testutil.StaticOracle(t, preimageValue) - goVm, state, contracts := setup(t, i, oracle) - step := state.GetStep() - - // Define LL-related params - llAddress := effAddr + v.effAddrOffset - llOwnerThread := state.GetCurrentThread().ThreadId - if !v.matchThreadId { - llOwnerThread += 1 - } - - // Set up state - state.PreimageKey = preimageKey - state.PreimageOffset = c.preimageOffset - state.GetRegistersRef()[2] = arch.SysRead - state.GetRegistersRef()[4] = exec.FdPreimageRead - state.GetRegistersRef()[5] = c.addr - state.GetRegistersRef()[6] = c.count - testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) - state.LLReservationStatus = v.llReservationStatus - state.LLAddress = llAddress - state.LLOwnerThread = llOwnerThread - state.GetMemory().SetWord(effAddr, c.prestateMem) - // Setup expectations - expected := mttestutil.NewExpectedMTState(state) - expected.ExpectStep() - expected.ActiveThread().Registers[2] = c.writeLen - expected.ActiveThread().Registers[7] = 0 // no error - expected.PreimageOffset += c.writeLen - expected.ExpectMemoryWordWrite(effAddr, c.postateMem) - if v.shouldClearReservation { - expected.LLReservationStatus = multithreaded.LLStatusNone - expected.LLAddress = 0 - expected.LLOwnerThread = 0 - } - - if c.shouldPanic { - require.Panics(t, func() { _, _ = goVm.Step(true) }) - testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts) - } else { - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) - } - }) - } - } + testMTSysReadPreimage(t, preimageValue, cases) } -func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { - llVariations := []struct { - name string - llReservationStatus multithreaded.LLReservationStatus - matchThreadId bool - effAddrOffset Word - shouldClearReservation bool - }{ - {name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true}, - {name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true}, - {name: "matching reservation, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: true, shouldClearReservation: true}, - {name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true}, - {name: "matching reservation, diff thread, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, shouldClearReservation: true}, - {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, - {name: "mismatched reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false}, - {name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true}, - {name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, - } - - rt := Word(0x12_34_56_78) - baseReg := 5 - rtReg := 6 - cases := []struct { - name string - opcode int - offset int - base Word - effAddr Word - preMem Word - postMem Word - }{ - {name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF}, - {name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x56_78_FF_FF}, - {name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78}, - {name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78}, - {name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF}, - } - for i, c := range cases { - for _, v := range llVariations { - tName := fmt.Sprintf("%v (%v)", c.name, v.name) - t.Run(tName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) - goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08)) - step := state.GetStep() - - // Define LL-related params - llAddress := c.effAddr + v.effAddrOffset - llOwnerThread := state.GetCurrentThread().ThreadId - if !v.matchThreadId { - llOwnerThread += 1 - } - - // Setup state - state.GetRegistersRef()[rtReg] = rt - state.GetRegistersRef()[baseReg] = c.base - testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) - state.GetMemory().SetWord(c.effAddr, c.preMem) - state.LLReservationStatus = v.llReservationStatus - state.LLAddress = llAddress - state.LLOwnerThread = llOwnerThread - - // Setup expectations - expected := mttestutil.NewExpectedMTState(state) - expected.ExpectStep() - expected.ExpectMemoryWordWrite(c.effAddr, c.postMem) - if v.shouldClearReservation { - expected.LLReservationStatus = multithreaded.LLStatusNone - expected.LLAddress = 0 - expected.LLOwnerThread = 0 - } - - stepWitness, err := goVm.Step(true) - require.NoError(t, err) - - // Check expectations - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) - }) - } +func TestEVM_MT_StoreOpsClearMemReservation32(t *testing.T) { + t.Parallel() + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_MT_StoreOpsClearMemReservation64") + + cases := []testMTStoreOpsClearMemReservationTestCase{ + {name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF}, + {name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x56_78_FF_FF}, + {name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78}, + {name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78}, + {name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF}, } + testMTStoreOpsClearMemReservation(t, cases) } func TestEVM_SysClone_FlagHandling(t *testing.T) { @@ -633,39 +483,38 @@ func TestEVM_PopExitedThread(t *testing.T) { } func TestEVM_SysFutex_WaitPrivate(t *testing.T) { + // Note: parameters are written as 64-bit values. For 32-bit architectures, these values are downcast to 32-bit cases := []struct { name string - addressParam Word - effAddr Word - targetValue Word - actualValue Word - timeout Word + addressParam uint64 + effAddr uint64 + targetValue uint64 + actualValue uint64 + timeout uint64 shouldFail bool shouldSetTimeout bool }{ - {name: "successful wait, no timeout", addressParam: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01}, - {name: "successful wait, no timeout, unaligned addr", addressParam: 0x1235, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01}, - {name: "memory mismatch, no timeout", addressParam: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, shouldFail: true}, - {name: "memory mismatch, no timeout, unaligned", addressParam: 0x1203, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, shouldFail: true}, - {name: "successful wait w timeout", addressParam: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01, timeout: 1000000, shouldSetTimeout: true}, - {name: "successful wait w timeout, unaligned", addressParam: 0x1232, effAddr: 0x1230, targetValue: 0x01, actualValue: 0x01, timeout: 1000000, shouldSetTimeout: true}, - {name: "memory mismatch w timeout", addressParam: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, timeout: 2000000, shouldFail: true}, - {name: "memory mismatch w timeout, unaligned", addressParam: 0x120F, effAddr: 0x120C, targetValue: 0x01, actualValue: 0x02, timeout: 2000000, shouldFail: true}, + {name: "successful wait, no timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_38, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01}, + {name: "successful wait, no timeout, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_12_39, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01}, + {name: "memory mismatch, no timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_00, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, shouldFail: true}, + {name: "memory mismatch, no timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_03, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, shouldFail: true}, + {name: "successful wait w timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_38, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01, timeout: 1000000, shouldSetTimeout: true}, + {name: "successful wait w timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_32, effAddr: 0xFF_FF_FF_FF_FF_FF_12_30, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01, timeout: 1000000, shouldSetTimeout: true}, + {name: "memory mismatch w timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_00, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, timeout: 2000000, shouldFail: true}, + {name: "memory mismatch w timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_0F, effAddr: 0xFF_FF_FF_FF_FF_FF_12_10, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, timeout: 2000000, shouldFail: true}, } - for i, c := range cases { t.Run(c.name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm, state, contracts := setup(t, i*1234, nil) step := state.GetStep() testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) - state.Memory.SetWord(c.effAddr, c.actualValue) + state.Memory.SetWord(Word(c.effAddr), Word(c.actualValue)) state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number - state.GetRegistersRef()[4] = c.addressParam + state.GetRegistersRef()[4] = Word(c.addressParam) state.GetRegistersRef()[5] = exec.FutexWaitPrivate - state.GetRegistersRef()[6] = c.targetValue - state.GetRegistersRef()[7] = c.timeout + state.GetRegistersRef()[6] = Word(c.targetValue) + state.GetRegistersRef()[7] = Word(c.timeout) // Setup expectations expected := mttestutil.NewExpectedMTState(state) @@ -678,8 +527,8 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { expected.ActiveThread().Registers[7] = exec.MipsEAGAIN } else { // PC and return registers should not update on success, updates happen when wait completes - expected.ActiveThread().FutexAddr = c.effAddr - expected.ActiveThread().FutexVal = c.targetValue + expected.ActiveThread().FutexAddr = Word(c.effAddr) + expected.ActiveThread().FutexVal = Word(c.targetValue) expected.ActiveThread().FutexTimeoutStep = exec.FutexNoTimeout if c.shouldSetTimeout { expected.ActiveThread().FutexTimeoutStep = step + exec.FutexTimeoutSteps + 1 @@ -687,9 +536,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { } // State transition - var err error - var stepWitness *mipsevm.StepWitness - stepWitness, err = goVm.Step(true) + stepWitness, err := goVm.Step(true) require.NoError(t, err) // Validate post-state @@ -700,39 +547,38 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { } func TestEVM_SysFutex_WakePrivate(t *testing.T) { + // Note: parameters are written as 64-bit values. For 32-bit architectures, these values are downcast to 32-bit cases := []struct { name string - addressParam Word - effAddr Word + addressParam uint64 + effAddr uint64 activeThreadCount int inactiveThreadCount int traverseRight bool expectTraverseRight bool }{ - {name: "Traverse right", addressParam: 0x6700, effAddr: 0x6700, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true}, - {name: "Traverse right, unaligned addr", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true}, - {name: "Traverse right, no left threads", addressParam: 0x6784, effAddr: 0x6784, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true}, - {name: "Traverse right, no left threads, unaligned addr", addressParam: 0x678E, effAddr: 0x678C, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true}, - {name: "Traverse right, single thread", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true}, - {name: "Traverse right, single thread, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true}, - {name: "Traverse left", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false}, - {name: "Traverse left, unaliagned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false}, - {name: "Traverse left, switch directions", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true}, - {name: "Traverse left, switch directions, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true}, - {name: "Traverse left, single thread", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true}, - {name: "Traverse left, single thread, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true}, + {name: "Traverse right", addressParam: 0xFF_FF_FF_FF_FF_FF_67_00, effAddr: 0xFF_FF_FF_FF_FF_FF_67_00, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true}, + {name: "Traverse right, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true}, + {name: "Traverse right, no left threads", addressParam: 0xFF_FF_FF_FF_FF_FF_67_84, effAddr: 0xFF_FF_FF_FF_FF_FF_67_84, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true}, + {name: "Traverse right, no left threads, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_67_8E, effAddr: 0xFF_FF_FF_FF_FF_FF_67_8C, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true}, + {name: "Traverse right, single thread", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true}, + {name: "Traverse right, single thread, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true}, + {name: "Traverse left", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false}, + {name: "Traverse left, unaliagned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false}, + {name: "Traverse left, switch directions", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true}, + {name: "Traverse left, switch directions, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true}, + {name: "Traverse left, single thread", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true}, + {name: "Traverse left, single thread, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true}, } - for i, c := range cases { t.Run(c.name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm, state, contracts := setup(t, i*1122, nil) mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount) step := state.Step testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number - state.GetRegistersRef()[4] = c.addressParam + state.GetRegistersRef()[4] = Word(c.addressParam) state.GetRegistersRef()[5] = exec.FutexWakePrivate // Set up post-state expectations @@ -740,7 +586,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { expected.ExpectStep() expected.ActiveThread().Registers[2] = 0 expected.ActiveThread().Registers[7] = 0 - expected.Wakeup = c.effAddr + expected.Wakeup = Word(c.effAddr) & arch.AddressMask // aligned for 32 and 64-bit compatibility expected.ExpectPreemption(state) expected.TraverseRight = c.expectTraverseRight if c.traverseRight != c.expectTraverseRight { @@ -750,9 +596,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { } // State transition - var err error - var stepWitness *mipsevm.StepWitness - stepWitness, err = goVm.Step(true) + stepWitness, err := goVm.Step(true) require.NoError(t, err) // Validate post-state @@ -760,6 +604,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) }) } + } func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { @@ -1103,70 +948,27 @@ var NoopSyscalls = map[string]uint32{ "SysTimerDelete": 4261, } -func TestEVM_NoopSyscall(t *testing.T) { - for noopName, noopVal := range NoopSyscalls { - t.Run(noopName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - goVm, state, contracts := setup(t, int(noopVal), nil) - - testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) - state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number - step := state.Step - - // Set up post-state expectations - expected := mttestutil.NewExpectedMTState(state) - expected.ExpectStep() - expected.ActiveThread().Registers[2] = 0 - expected.ActiveThread().Registers[7] = 0 - - // State transition - var err error - var stepWitness *mipsevm.StepWitness - stepWitness, err = goVm.Step(true) - require.NoError(t, err) - - // Validate post-state - expected.Validate(t, state) - testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) - }) - - } +func TestEVM_NoopSyscall32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_NoopSyscall64") + testNoopSyscall(t, NoopSyscalls) } -func TestEVM_UnsupportedSyscall(t *testing.T) { +func TestEVM_UnsupportedSyscall32(t *testing.T) { + testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_UnsupportedSyscall64") t.Parallel() - var tracer *tracing.Hooks - var NoopSyscallNums = maps.Values(NoopSyscalls) - var SupportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid} + var noopSyscallNums = maps.Values(NoopSyscalls) + var supportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid} unsupportedSyscalls := make([]uint32, 0, 400) for i := 4000; i < 4400; i++ { candidate := uint32(i) - if slices.Contains(SupportedSyscalls, candidate) || slices.Contains(NoopSyscallNums, candidate) { + if slices.Contains(supportedSyscalls, candidate) || slices.Contains(noopSyscallNums, candidate) { continue } unsupportedSyscalls = append(unsupportedSyscalls, candidate) } - for i, syscallNum := range unsupportedSyscalls { - testName := fmt.Sprintf("Unsupported syscallNum %v", syscallNum) - i := i - syscallNum := syscallNum - t.Run(testName, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) - t.Parallel() - goVm, state, contracts := setup(t, i*3434, nil) - // Setup basic getThreadId syscall instruction - testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) - state.GetRegistersRef()[2] = Word(syscallNum) - proofData := multiThreadedProofGenerator(t, state) - // Set up post-state expectations - require.Panics(t, func() { _, _ = goVm.Step(true) }) - - errorMessage := "MIPS2: unimplemented syscall" - testutil.AssertEVMReverts(t, state, contracts, tracer, proofData, testutil.CreateErrorStringMatcher(errorMessage)) - }) - } + testUnsupportedSyscall(t, unsupportedSyscalls) } func TestEVM_EmptyThreadStacks(t *testing.T) { diff --git a/cannon/mipsevm/tests/fuzz_evm_common_test.go b/cannon/mipsevm/tests/fuzz_evm_common_test.go index c58e1421a9e1..c637cbbe19a0 100644 --- a/cannon/mipsevm/tests/fuzz_evm_common_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_common_test.go @@ -19,20 +19,11 @@ import ( const syscallInsn = uint32(0x00_00_00_0c) -func FuzzStateSyscallBrk32(f *testing.F) { - doFuzzStateSyscallBrk(f) -} - -func FuzzStateSyscallBrk64(f *testing.F) { - doFuzzStateSyscallBrk(f) -} - -func doFuzzStateSyscallBrk(f *testing.F) { +func FuzzStateSyscallBrk(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() state.GetRegistersRef()[2] = arch.SysBrk @@ -57,15 +48,7 @@ func doFuzzStateSyscallBrk(f *testing.F) { }) } -func FuzzStateSyscallMmap32(f *testing.F) { - doFuzzStateSyscallMmap(f) -} - -func FuzzStateSyscallMmap64(f *testing.F) { - doFuzzStateSyscallMmap(f) -} - -func doFuzzStateSyscallMmap(f *testing.F) { +func FuzzStateSyscallMmap(f *testing.F) { // Add special cases for large memory allocation f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2)) @@ -76,7 +59,6 @@ func doFuzzStateSyscallMmap(f *testing.F) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed), testutil.WithHeap(heap)) state := goVm.GetState() @@ -121,20 +103,11 @@ func doFuzzStateSyscallMmap(f *testing.F) { }) } -func FuzzStateSyscallExitGroup32(f *testing.F) { - doFuzzStateSyscallExitGroup(f) -} - -func FuzzStateSyscallExitGroup64(f *testing.F) { - doFuzzStateSyscallExitGroup(f) -} - -func doFuzzStateSyscallExitGroup(f *testing.F) { +func FuzzStateSyscallExitGroup(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() @@ -159,20 +132,11 @@ func doFuzzStateSyscallExitGroup(f *testing.F) { }) } -func FuzzStateSyscallFcntl32(f *testing.F) { - doFuzzStateSyscallFcntl(f) -} - -func FuzzStateSyscallFcntl64(f *testing.F) { - doFuzzStateSyscallFcntl(f) -} - -func doFuzzStateSyscallFcntl(f *testing.F) { +func FuzzStateSyscallFcntl(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := goVm.GetState() @@ -193,7 +157,7 @@ func doFuzzStateSyscallFcntl(f *testing.F) { expected.Registers[2] = 0 expected.Registers[7] = 0 default: - expected.Registers[2] = 0xFF_FF_FF_FF + expected.Registers[2] = ^Word(0) expected.Registers[7] = exec.MipsEBADF } } else if cmd == 3 { @@ -205,11 +169,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) { expected.Registers[2] = 1 expected.Registers[7] = 0 default: - expected.Registers[2] = 0xFF_FF_FF_FF + expected.Registers[2] = ^Word(0) expected.Registers[7] = exec.MipsEBADF } } else { - expected.Registers[2] = 0xFF_FF_FF_FF + expected.Registers[2] = ^Word(0) expected.Registers[7] = exec.MipsEINVAL } @@ -224,20 +188,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) { }) } -func FuzzStateHintRead32(f *testing.F) { - doFuzzStateHintRead(f) -} - -func FuzzStateHintRead64(f *testing.F) { - doFuzzStateHintRead(f) -} - -func doFuzzStateHintRead(f *testing.F) { +func FuzzStateHintRead(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) preimageData := []byte("hello world") preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() oracle := testutil.StaticOracle(t, preimageData) // only used for hinting @@ -270,15 +225,7 @@ func doFuzzStateHintRead(f *testing.F) { }) } -func FuzzStatePreimageRead32(f *testing.F) { - doFuzzStatePreimageRead(f) -} - -func FuzzStatePreimageRead64(f *testing.F) { - doFuzzStatePreimageRead(f) -} - -func doFuzzStatePreimageRead(f *testing.F) { +func FuzzStatePreimageRead(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) { for _, v := range versions { @@ -342,20 +289,11 @@ func doFuzzStatePreimageRead(f *testing.F) { }) } -func FuzzStateHintWrite32(f *testing.F) { - doFuzzStateHintWrite(f) -} - -func FuzzStateHintWrite64(f *testing.F) { - doFuzzStateHintWrite(f) -} - -func doFuzzStateHintWrite(f *testing.F) { +func FuzzStateHintWrite(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) // Make sure pc does not overlap with hint data in memory pc := Word(0) if addr <= 8 { @@ -433,27 +371,18 @@ func doFuzzStateHintWrite(f *testing.F) { }) } -func FuzzStatePreimageWrite32(f *testing.F) { - doFuzzStatePreimageWrite(f) -} - -func FuzzStatePreimageWrite64(f *testing.F) { - doFuzzStatePreimageWrite(f) -} - -func doFuzzStatePreimageWrite(f *testing.F) { +func FuzzStatePreimageWrite(f *testing.F) { versions := GetMipsVersionTestCases(f) f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) { for _, v := range versions { t.Run(v.Name, func(t *testing.T) { - testutil.TemporarilySkip64BitTests(t) // Make sure pc does not overlap with preimage data in memory pc := Word(0) if addr <= 8 { addr += 8 } effAddr := addr & arch.AddressMask - preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78} + preexistingMemoryVal := [8]byte{0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21} preimageData := []byte("hello world") preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() oracle := testutil.StaticOracle(t, preimageData) @@ -471,7 +400,7 @@ func doFuzzStatePreimageWrite(f *testing.F) { expectBytesWritten := count alignment := addr & arch.ExtMask - sz := 4 - alignment + sz := arch.WordSizeBytes - alignment if sz < expectBytesWritten { expectBytesWritten = sz } diff --git a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go index 9d4bb46a4271..49f45a4a5f2c 100644 --- a/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_multithreaded_test.go @@ -13,18 +13,9 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func FuzzStateSyscallCloneMT32(f *testing.F) { - doFuzzStateSyscallCloneMT(f) -} - -func FuzzStateSyscallCloneMT64(f *testing.F) { - doFuzzStateSyscallCloneMT(f) -} - -func doFuzzStateSyscallCloneMT(f *testing.F) { +func FuzzStateSyscallCloneMT(f *testing.F) { v := GetMultiThreadedTestCase(f) f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) { - testutil.TemporarilySkip64BitTests(t) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) state := mttestutil.GetMtState(t, goVm) // Update existing threads to avoid collision with nextThreadId diff --git a/cannon/mipsevm/tests/testfuncs_test.go b/cannon/mipsevm/tests/testfuncs_test.go new file mode 100644 index 000000000000..0087ab445d73 --- /dev/null +++ b/cannon/mipsevm/tests/testfuncs_test.go @@ -0,0 +1,502 @@ +package tests + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" + mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" + preimage "github.com/ethereum-optimism/optimism/op-preimage" + "github.com/ethereum/go-ethereum/crypto" +) + +type operatorTestCase struct { + name string + isImm bool + rs Word + rt Word + imm uint16 + funct uint32 + opcode uint32 + expectRes Word +} + +func testOperators(t *testing.T, cases []operatorTestCase, mips32Insn bool) { + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + // sign extend inputs for 64-bit compatibility + if mips32Insn { + tt.rs = randomizeUpperWord(signExtend64(tt.rs)) + tt.rt = randomizeUpperWord(signExtend64(tt.rt)) + tt.expectRes = signExtend64(tt.expectRes) + } + + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts) + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) + state := goVm.GetState() + var insn uint32 + var baseReg uint32 = 17 + var rtReg uint32 + var rdReg uint32 + if tt.isImm { + rtReg = 8 + insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[baseReg] = tt.rs + } else { + rtReg = 18 + rdReg = 8 + insn = baseReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct + state.GetRegistersRef()[baseReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + } + testutil.StoreInstruction(state.GetMemory(), 0, insn) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.Step += 1 + expected.PC = 4 + expected.NextPC = 8 + if tt.isImm { + expected.Registers[rtReg] = tt.expectRes + } else { + expected.Registers[rdReg] = tt.expectRes + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + validator.ValidateEVM(t, stepWitness, step, goVm) + }) + } + } +} + +type mulDivTestCase struct { + name string + rs Word + rt Word + funct uint32 + opcode uint32 + expectHi Word + expectLo Word + expectRes Word + rdReg uint32 + panicMsg string + revertMsg string +} + +func testMulDiv(t *testing.T, cases []mulDivTestCase, mips32Insn bool) { + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + if mips32Insn { + tt.rs = randomizeUpperWord(signExtend64(tt.rs)) + tt.rt = randomizeUpperWord(signExtend64(tt.rt)) + tt.expectHi = signExtend64(tt.expectHi) + tt.expectLo = signExtend64(tt.expectLo) + tt.expectRes = signExtend64(tt.expectRes) + } + + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) + state := goVm.GetState() + var insn uint32 + baseReg := uint32(0x9) + rtReg := uint32(0xa) + + insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.rdReg<<11 | tt.funct + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[baseReg] = tt.rs + testutil.StoreInstruction(state.GetMemory(), 0, insn) + + if tt.panicMsg != "" { + proofData := v.ProofGenerator(t, goVm.GetState()) + require.PanicsWithValue(t, tt.panicMsg, func() { + _, _ = goVm.Step( + false) + }) + testutil.AssertEVMReverts(t, state, v.Contracts, nil, proofData, testutil.CreateErrorStringMatcher(tt.revertMsg)) + return + } + + step := state.GetStep() + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + if tt.expectRes != 0 { + expected.Registers[tt.rdReg] = tt.expectRes + } else { + expected.HI = tt.expectHi + expected.LO = tt.expectLo + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +type loadStoreTestCase struct { + name string + rt Word + base Word + imm uint32 + opcode uint32 + memVal Word + expectMemVal Word + expectRes Word +} + +func testLoadStore(t *testing.T, cases []loadStoreTestCase) { + baseReg := uint32(9) + rtReg := uint32(8) + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + addr := tt.base + Word(tt.imm) + effAddr := arch.AddressMask & addr + + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + + insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[baseReg] = tt.base + + testutil.StoreInstruction(state.GetMemory(), 0, insn) + state.GetMemory().SetWord(effAddr, tt.memVal) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + if tt.expectMemVal != 0 { + expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal) + } else { + expected.Registers[rtReg] = tt.expectRes + } + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } +} + +type branchTestCase struct { + name string + pc Word + expectNextPC Word + opcode uint32 + regimm uint32 + expectLink bool + rs arch.SignedInteger + offset uint16 +} + +func testBranch(t *testing.T, cases []branchTestCase) { + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) + state := goVm.GetState() + const rsReg = 8 // t0 + insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset) + testutil.StoreInstruction(state.GetMemory(), tt.pc, insn) + state.GetRegistersRef()[rsReg] = Word(tt.rs) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.Step += 1 + expected.PC = state.GetCpu().NextPC + expected.NextPC = tt.expectNextPC + if tt.expectLink { + expected.Registers[31] = state.GetPC() + 8 + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +type testMTStoreOpsClearMemReservationTestCase struct { + // name is the test name + name string + // opcode is the instruction opcode + opcode uint32 + // offset is the immediate offset encoded in the instruction + offset uint32 + // base is the base/rs register prestate + base Word + // effAddr is the address used to set the prestate preMem value. It is also used as the base LLAddress that can be adjusted reservation assertions + effAddr Word + // premem is the prestate value of the word located at effrAddr + preMem Word + // postMem is the expected post-state value of the word located at effAddr + postMem Word +} + +func testMTStoreOpsClearMemReservation(t *testing.T, cases []testMTStoreOpsClearMemReservationTestCase) { + llVariations := []struct { + name string + llReservationStatus multithreaded.LLReservationStatus + matchThreadId bool + effAddrOffset Word + shouldClearReservation bool + }{ + {name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true}, + {name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true}, + {name: "matching reservation, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: true, shouldClearReservation: true}, + {name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true}, + {name: "matching reservation, diff thread, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, shouldClearReservation: true}, + {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, + {name: "mismatched reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false}, + {name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true}, + {name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, + } + + rt := Word(0x12_34_56_78) + //rt := Word(0x12_34_56_78_12_34_56_78) + baseReg := uint32(5) + rtReg := uint32(6) + for i, c := range cases { + for _, v := range llVariations { + tName := fmt.Sprintf("%v (%v)", c.name, v.name) + t.Run(tName, func(t *testing.T) { + t.Parallel() + insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) + goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08)) + step := state.GetStep() + + // Define LL-related params + llAddress := c.effAddr + v.effAddrOffset + llOwnerThread := state.GetCurrentThread().ThreadId + if !v.matchThreadId { + llOwnerThread += 1 + } + + // Setup state + state.GetRegistersRef()[rtReg] = rt + state.GetRegistersRef()[baseReg] = c.base + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetMemory().SetWord(c.effAddr, c.preMem) + state.LLReservationStatus = v.llReservationStatus + state.LLAddress = llAddress + state.LLOwnerThread = llOwnerThread + + // Setup expectations + expected := mttestutil.NewExpectedMTState(state) + expected.ExpectStep() + expected.ExpectMemoryWordWrite(c.effAddr, c.postMem) + if v.shouldClearReservation { + expected.LLReservationStatus = multithreaded.LLStatusNone + expected.LLAddress = 0 + expected.LLOwnerThread = 0 + } + + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) + }) + } + } +} + +type testMTSysReadPreimageTestCase struct { + name string + addr Word + count Word + writeLen Word + preimageOffset Word + prestateMem Word + postateMem Word + shouldPanic bool +} + +func testMTSysReadPreimage(t *testing.T, preimageValue []byte, cases []testMTSysReadPreimageTestCase) { + llVariations := []struct { + name string + llReservationStatus multithreaded.LLReservationStatus + matchThreadId bool + effAddrOffset Word + shouldClearReservation bool + }{ + {name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true}, + {name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true}, + {name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true}, + {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, + {name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false}, + {name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true}, + {name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false}, + } + + for i, c := range cases { + for _, v := range llVariations { + tName := fmt.Sprintf("%v (%v)", c.name, v.name) + t.Run(tName, func(t *testing.T) { + t.Parallel() + effAddr := arch.AddressMask & c.addr + preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() + oracle := testutil.StaticOracle(t, preimageValue) + goVm, state, contracts := setup(t, i, oracle) + step := state.GetStep() + + // Define LL-related params + llAddress := effAddr + v.effAddrOffset + llOwnerThread := state.GetCurrentThread().ThreadId + if !v.matchThreadId { + llOwnerThread += 1 + } + + // Set up state + state.PreimageKey = preimageKey + state.PreimageOffset = c.preimageOffset + state.GetRegistersRef()[2] = arch.SysRead + state.GetRegistersRef()[4] = exec.FdPreimageRead + state.GetRegistersRef()[5] = c.addr + state.GetRegistersRef()[6] = c.count + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) + state.LLReservationStatus = v.llReservationStatus + state.LLAddress = llAddress + state.LLOwnerThread = llOwnerThread + state.GetMemory().SetWord(effAddr, c.prestateMem) + + // Setup expectations + expected := mttestutil.NewExpectedMTState(state) + expected.ExpectStep() + expected.ActiveThread().Registers[2] = c.writeLen + expected.ActiveThread().Registers[7] = 0 // no error + expected.PreimageOffset += c.writeLen + expected.ExpectMemoryWordWrite(effAddr, c.postateMem) + if v.shouldClearReservation { + expected.LLReservationStatus = multithreaded.LLStatusNone + expected.LLAddress = 0 + expected.LLOwnerThread = 0 + } + + if c.shouldPanic { + require.Panics(t, func() { _, _ = goVm.Step(true) }) + testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts) + } else { + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) + } + }) + } + } +} + +func testNoopSyscall(t *testing.T, syscalls map[string]uint32) { + for noopName, noopVal := range syscalls { + t.Run(noopName, func(t *testing.T) { + t.Parallel() + goVm, state, contracts := setup(t, int(noopVal), nil) + + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) + state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number + step := state.Step + + // Set up post-state expectations + expected := mttestutil.NewExpectedMTState(state) + expected.ExpectStep() + expected.ActiveThread().Registers[2] = 0 + expected.ActiveThread().Registers[7] = 0 + + // State transition + stepWitness, err := goVm.Step(true) + require.NoError(t, err) + + // Validate post-state + expected.Validate(t, state) + testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) + }) + } +} + +func testUnsupportedSyscall(t *testing.T, unsupportedSyscalls []uint32) { + for i, syscallNum := range unsupportedSyscalls { + testName := fmt.Sprintf("Unsupported syscallNum %v", syscallNum) + i := i + syscallNum := syscallNum + t.Run(testName, func(t *testing.T) { + t.Parallel() + goVm, state, contracts := setup(t, i*3434, nil) + // Setup basic getThreadId syscall instruction + testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) + state.GetRegistersRef()[2] = Word(syscallNum) + proofData := multiThreadedProofGenerator(t, state) + // Set up post-state expectations + require.Panics(t, func() { _, _ = goVm.Step(true) }) + + errorMessage := "unimplemented syscall" + testutil.AssertEVMReverts(t, state, contracts, nil, proofData, testutil.CreateErrorStringMatcher(errorMessage)) + }) + } +} + +// signExtend64 is used to sign-extend 32-bit words for 64-bit compatibility +func signExtend64(w Word) Word { + if arch.IsMips32 { + return w + } else { + return exec.SignExtend(w, 32) + } +} + +const seed = 0xdead + +var rand = testutil.NewRandHelper(seed) + +// randomizeUpperWord is used to assert that 32-bit operations use the lower word only +func randomizeUpperWord(w Word) Word { + if arch.IsMips32 { + return w + } else { + if w>>32 == 0x0 { // nolint:staticcheck + rnd := rand.Uint32() + upper := uint64(rnd) << 32 + return Word(upper | uint64(uint32(w))) + } else { + return w + } + } +} diff --git a/cannon/mipsevm/testutil/arch.go b/cannon/mipsevm/testutil/arch.go index a69896c041e5..b4eb50b7e3f1 100644 --- a/cannon/mipsevm/testutil/arch.go +++ b/cannon/mipsevm/testutil/arch.go @@ -4,6 +4,7 @@ package testutil import ( "bytes" + "testing" "github.com/stretchr/testify/require" @@ -46,3 +47,10 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui func ToSignedInteger(x Word) arch.SignedInteger { return arch.SignedInteger(x) } + +// Cannon32OnlyTest skips the test if it's not a cannon64 build +func Cannon32OnlyTest(t testing.TB, msg string, args ...any) { + if !arch.IsMips32 { + t.Skipf(msg, args...) + } +} diff --git a/cannon/mipsevm/testutil/vmtests.go b/cannon/mipsevm/testutil/vmtests.go index b16550e446c7..333720a4ec02 100644 --- a/cannon/mipsevm/testutil/vmtests.go +++ b/cannon/mipsevm/testutil/vmtests.go @@ -141,10 +141,3 @@ func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.Create require.Equal(t, expectedStdOut, stdOutBuf.String(), "stdout") require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr") } - -func TemporarilySkip64BitTests(t *testing.T) { - if !arch.IsMips32 { - // TODO(#12598) Update and enable these tests - t.Skip("Skipping 64-bit test") - } -} From 9f3f8cbc20337a07840ff59d9e4c5f991766a82c Mon Sep 17 00:00:00 2001 From: Joshua Gutow Date: Tue, 12 Nov 2024 18:18:19 +0700 Subject: [PATCH 169/451] Add tool to reconstruct L2 deposit tx hash (#12893) --- op-chain-ops/cmd/deposit-hash/main.go | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 op-chain-ops/cmd/deposit-hash/main.go diff --git a/op-chain-ops/cmd/deposit-hash/main.go b/op-chain-ops/cmd/deposit-hash/main.go new file mode 100644 index 000000000000..9166a0667c09 --- /dev/null +++ b/op-chain-ops/cmd/deposit-hash/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "context" + "flag" + "fmt" + + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" +) + +func main() { + var rpcURL, txHash string + flag.StringVar(&rpcURL, "rpc", "", "L1 RPC URL") + flag.StringVar(&txHash, "tx", "", "Deposit transaction hash on L1") + flag.Parse() + + depositLogTopic := common.HexToHash("0xb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32") + + ethClient, err := ethclient.Dial(rpcURL) + if err != nil { + log.Crit("Error creating RPC", "err", err) + } + + l1Receipt, err := ethClient.TransactionReceipt(context.TODO(), common.HexToHash(txHash)) + if err != nil { + log.Crit("Error fetching transaction", "err", err) + } + + for _, ethLog := range l1Receipt.Logs { + if ethLog.Topics[0].String() == depositLogTopic.String() { + + reconstructedDep, err := derive.UnmarshalDepositLogEvent(ethLog) + if err != nil { + log.Crit("Failed to parse deposit event ", "err", err) + } + tx := types.NewTx(reconstructedDep) + fmt.Println("L2 Tx Hash", tx.Hash().String()) + } + } +} From 72e67e53b18e697b6e421e9eba303fb821064a8f Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 12 Nov 2024 23:38:16 +0700 Subject: [PATCH 170/451] op-bootnode: remove op-bootnode service (#12901) --- README.md | 1 - op-bootnode/.gitignore | 1 - op-bootnode/Makefile | 22 --- op-bootnode/bootnode/entrypoint.go | 134 ------------------ op-bootnode/cmd/main.go | 28 ---- op-bootnode/flags/flags.go | 25 ---- .../op-stack-go/Dockerfile.dockerignore | 1 - 7 files changed, 212 deletions(-) delete mode 100644 op-bootnode/.gitignore delete mode 100644 op-bootnode/Makefile delete mode 100644 op-bootnode/bootnode/entrypoint.go delete mode 100644 op-bootnode/cmd/main.go delete mode 100644 op-bootnode/flags/flags.go diff --git a/README.md b/README.md index 42a880f5f427..bda740f63c2c 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,6 @@ The Optimism Immunefi program offers up to $2,000,042 for in-scope critical vuln
 ├── docs: A collection of documents including audits and post-mortems
 ├── op-batcher: L2-Batch Submitter, submits bundles of batches to L1
-├── op-bootnode: Standalone op-node discovery bootnode
 ├── op-chain-ops: State surgery utilities
 ├── op-challenger: Dispute game challenge agent
 ├── op-e2e: End-to-End testing of all bedrock components in Go
diff --git a/op-bootnode/.gitignore b/op-bootnode/.gitignore
deleted file mode 100644
index ba077a4031ad..000000000000
--- a/op-bootnode/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-bin
diff --git a/op-bootnode/Makefile b/op-bootnode/Makefile
deleted file mode 100644
index f9f276e9ed11..000000000000
--- a/op-bootnode/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-GITCOMMIT ?= $(shell git rev-parse HEAD)
-GITDATE ?= $(shell git show -s --format='%ct')
-VERSION ?= v0.0.0
-
-LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
-LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
-LDFLAGSSTRING +=-X main.Version=$(VERSION)
-LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
-
-op-bootnode:
-	env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/op-bootnode ./cmd
-
-clean:
-	rm -f bin/op-bootnode
-
-test:
-	go test -v ./...
-
-.PHONY: \
-	op-bootnode \
-	clean \
-	test
diff --git a/op-bootnode/bootnode/entrypoint.go b/op-bootnode/bootnode/entrypoint.go
deleted file mode 100644
index 0c33383c70c7..000000000000
--- a/op-bootnode/bootnode/entrypoint.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package bootnode
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/urfave/cli/v2"
-
-	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/rpc"
-
-	opnode "github.com/ethereum-optimism/optimism/op-node"
-	"github.com/ethereum-optimism/optimism/op-node/metrics"
-	"github.com/ethereum-optimism/optimism/op-node/p2p"
-	p2pcli "github.com/ethereum-optimism/optimism/op-node/p2p/cli"
-	"github.com/ethereum-optimism/optimism/op-node/rollup"
-	"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
-	"github.com/ethereum-optimism/optimism/op-service/eth"
-	oplog "github.com/ethereum-optimism/optimism/op-service/log"
-	opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
-	oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
-)
-
-type gossipNoop struct{}
-
-func (g *gossipNoop) OnUnsafeL2Payload(_ context.Context, _ peer.ID, _ *eth.ExecutionPayloadEnvelope) error {
-	return nil
-}
-
-type gossipConfig struct{}
-
-func (g *gossipConfig) P2PSequencerAddress() common.Address {
-	return common.Address{}
-}
-
-type l2Chain struct{}
-
-func (l *l2Chain) PayloadByNumber(_ context.Context, _ uint64) (*eth.ExecutionPayloadEnvelope, error) {
-	return nil, errors.New("P2P req/resp is not supported in bootnodes")
-}
-
-func Main(cliCtx *cli.Context) error {
-	log.Info("Initializing bootnode")
-	logCfg := oplog.ReadCLIConfig(cliCtx)
-	logger := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
-	oplog.SetGlobalLogHandler(logger.Handler())
-	m := metrics.NewMetrics("default")
-	ctx := context.Background()
-
-	config, err := opnode.NewRollupConfigFromCLI(logger, cliCtx)
-	if err != nil {
-		return err
-	}
-	if err = validateConfig(config); err != nil {
-		return err
-	}
-
-	p2pConfig, err := p2pcli.NewConfig(cliCtx, config)
-	if err != nil {
-		return fmt.Errorf("failed to load p2p config: %w", err)
-	}
-	if p2pConfig.EnableReqRespSync {
-		logger.Warn("req-resp sync is enabled, bootnode does not support this feature")
-		p2pConfig.EnableReqRespSync = false
-	}
-
-	p2pNode, err := p2p.NewNodeP2P(ctx, config, logger, p2pConfig, &gossipNoop{}, &l2Chain{}, &gossipConfig{}, m, false)
-	if err != nil || p2pNode == nil {
-		return err
-	}
-	if p2pNode.Dv5Udp() == nil {
-		return fmt.Errorf("uninitialized discovery service")
-	}
-
-	rpcCfg := oprpc.ReadCLIConfig(cliCtx)
-	if err := rpcCfg.Check(); err != nil {
-		return fmt.Errorf("failed to validate RPC config")
-	}
-	rpcServer := oprpc.NewServer(rpcCfg.ListenAddr, rpcCfg.ListenPort, "", oprpc.WithLogger(logger))
-	if rpcCfg.EnableAdmin {
-		logger.Info("Admin RPC enabled but does nothing for the bootnode")
-	}
-	rpcServer.AddAPI(rpc.API{
-		Namespace:     p2p.NamespaceRPC,
-		Version:       "",
-		Service:       p2p.NewP2PAPIBackend(p2pNode, logger, m),
-		Authenticated: false,
-	})
-	if err := rpcServer.Start(); err != nil {
-		return fmt.Errorf("failed to start the RPC server")
-	}
-	defer func() {
-		if err := rpcServer.Stop(); err != nil {
-			log.Error("failed to stop RPC server", "err", err)
-		}
-	}()
-
-	go p2pNode.DiscoveryProcess(ctx, logger, config, p2pConfig.TargetPeers())
-
-	metricsCfg := opmetrics.ReadCLIConfig(cliCtx)
-	if metricsCfg.Enabled {
-		log.Debug("starting metrics server", "addr", metricsCfg.ListenAddr, "port", metricsCfg.ListenPort)
-		metricsSrv, err := m.StartServer(metricsCfg.ListenAddr, metricsCfg.ListenPort)
-		if err != nil {
-			return fmt.Errorf("failed to start metrics server: %w", err)
-		}
-		defer func() {
-			if err := metricsSrv.Stop(context.Background()); err != nil {
-				log.Error("failed to stop metrics server", "err", err)
-			}
-		}()
-		log.Info("started metrics server", "addr", metricsSrv.Addr())
-		m.RecordUp()
-	}
-
-	return ctxinterrupt.Wait(ctx)
-}
-
-// validateConfig ensures the minimal config required to run a bootnode
-func validateConfig(config *rollup.Config) error {
-	if config.L2ChainID == nil || config.L2ChainID.Uint64() == 0 {
-		return errors.New("chain ID is not set")
-	}
-	if config.Genesis.L2Time <= 0 {
-		return errors.New("genesis timestamp is not set")
-	}
-	if config.BlockTime <= 0 {
-		return errors.New("block time is not set")
-	}
-	return nil
-}
diff --git a/op-bootnode/cmd/main.go b/op-bootnode/cmd/main.go
deleted file mode 100644
index 1dbc82010cfc..000000000000
--- a/op-bootnode/cmd/main.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
-	"os"
-
-	"github.com/ethereum/go-ethereum/log"
-	"github.com/urfave/cli/v2"
-
-	"github.com/ethereum-optimism/optimism/op-bootnode/bootnode"
-	"github.com/ethereum-optimism/optimism/op-bootnode/flags"
-	oplog "github.com/ethereum-optimism/optimism/op-service/log"
-)
-
-func main() {
-	oplog.SetupDefaults()
-
-	app := cli.NewApp()
-	app.Flags = flags.Flags
-	app.Name = "bootnode"
-	app.Usage = "Rollup Bootnode"
-	app.Description = "Broadcasts incoming P2P peers to each other, enabling peer bootstrapping."
-	app.Action = bootnode.Main
-
-	err := app.Run(os.Args)
-	if err != nil {
-		log.Crit("Application failed", "message", err)
-	}
-}
diff --git a/op-bootnode/flags/flags.go b/op-bootnode/flags/flags.go
deleted file mode 100644
index 5fbe0d538b33..000000000000
--- a/op-bootnode/flags/flags.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package flags
-
-import (
-	"github.com/urfave/cli/v2"
-
-	"github.com/ethereum-optimism/optimism/op-node/flags"
-	opflags "github.com/ethereum-optimism/optimism/op-service/flags"
-	oplog "github.com/ethereum-optimism/optimism/op-service/log"
-	opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
-	oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
-)
-
-const envVarPrefix = "OP_BOOTNODE"
-
-var Flags = []cli.Flag{
-	opflags.CLINetworkFlag(envVarPrefix, ""),
-	opflags.CLIRollupConfigFlag(envVarPrefix, ""),
-}
-
-func init() {
-	Flags = append(Flags, flags.P2PFlags(envVarPrefix)...)
-	Flags = append(Flags, opmetrics.CLIFlags(envVarPrefix)...)
-	Flags = append(Flags, oplog.CLIFlags(envVarPrefix)...)
-	Flags = append(Flags, oprpc.CLIFlags(envVarPrefix)...)
-}
diff --git a/ops/docker/op-stack-go/Dockerfile.dockerignore b/ops/docker/op-stack-go/Dockerfile.dockerignore
index e5eceaea73b8..bd700e291e49 100644
--- a/ops/docker/op-stack-go/Dockerfile.dockerignore
+++ b/ops/docker/op-stack-go/Dockerfile.dockerignore
@@ -4,7 +4,6 @@
 
 !/cannon
 !/op-batcher
-!/op-bootnode
 !/op-chain-ops
 !/op-deployer
 !/op-challenger

From 816885d42faa2c6b3ef40a2365ad322b791bbe1e Mon Sep 17 00:00:00 2001
From: Michael Amadi 
Date: Wed, 13 Nov 2024 05:42:37 +0100
Subject: [PATCH 171/451] improve contracts code coverage (#12900)

* improve L1 contracts code cov

* improve L1 contracts code cov

* improve L1 contracts code cov

* improve L1 contracts code cov

* improve L1 contracts code cov

* improve L1 contracts code cov
---
 .../test/L1/DataAvailabilityChallenge.t.sol   | 120 +++++++++++++++---
 .../test/L1/L2OutputOracle.t.sol              |  20 +++
 .../test/L1/OptimismPortal.t.sol              |  75 ++++++++++-
 .../contracts-bedrock/test/setup/Events.sol   |   2 +
 4 files changed, 199 insertions(+), 18 deletions(-)

diff --git a/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol b/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol
index ddbacec67c3e..a2ab917a0d2b 100644
--- a/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol
+++ b/packages/contracts-bedrock/test/L1/DataAvailabilityChallenge.t.sol
@@ -52,6 +52,26 @@ contract DataAvailabilityChallengeTest is CommonTest {
         assertEq(sender.balance, amount);
     }
 
+    function test_withdraw_fails_reverts(address sender, uint256 amount) public {
+        assumePayable(sender);
+        assumeNotPrecompile(sender);
+        // EntryPoint will revert if using amount > type(uint112).max.
+        vm.assume(sender != Preinstalls.EntryPoint_v060);
+        vm.assume(sender != address(dataAvailabilityChallenge));
+        vm.assume(sender.balance == 0);
+        vm.deal(sender, amount);
+
+        vm.prank(sender);
+        dataAvailabilityChallenge.deposit{ value: amount }();
+
+        assertEq(dataAvailabilityChallenge.balances(sender), amount);
+        assertEq(sender.balance, 0);
+
+        vm.etch(sender, hex"fe");
+        vm.expectRevert(abi.encodeWithSelector(IDataAvailabilityChallenge.WithdrawalFailed.selector));
+        dataAvailabilityChallenge.withdraw();
+    }
+
     function test_challenge_succeeds(
         address challenger,
         uint256 challengedBlockNumber,
@@ -220,6 +240,7 @@ contract DataAvailabilityChallengeTest is CommonTest {
         bytes memory preImage,
         uint256 challengedBlockNumber,
         uint256 resolverRefundPercentage,
+        uint64 bondSize,
         uint128 txGasPrice
     )
         public
@@ -229,6 +250,9 @@ contract DataAvailabilityChallengeTest is CommonTest {
         vm.assume(resolver != address(0));
         vm.assume(challenger != resolver);
 
+        vm.prank(dataAvailabilityChallenge.owner());
+        dataAvailabilityChallenge.setBondSize(bondSize);
+
         // Bound the resolver refund percentage to 100
         resolverRefundPercentage = bound(resolverRefundPercentage, 0, 100);
 
@@ -251,7 +275,6 @@ contract DataAvailabilityChallengeTest is CommonTest {
         vm.roll(challengedBlockNumber + 1);
 
         // Challenge the hash
-        uint256 bondSize = dataAvailabilityChallenge.bondSize();
         vm.deal(challenger, bondSize);
         vm.prank(challenger);
         dataAvailabilityChallenge.challenge{ value: bondSize }(challengedBlockNumber, challengedCommitment);
@@ -259,6 +282,26 @@ contract DataAvailabilityChallengeTest is CommonTest {
         // Store the address(0) balance before resolving to assert the burned amount later
         uint256 zeroAddressBalanceBeforeResolve = address(0).balance;
 
+        // Assert challenger balance after bond distribution
+        uint256 resolutionCost = (
+            dataAvailabilityChallenge.fixedResolutionCost()
+                + preImage.length * dataAvailabilityChallenge.variableResolutionCost()
+                    / dataAvailabilityChallenge.variableResolutionCostPrecision()
+        ) * block.basefee;
+        uint256 challengerRefund = bondSize > resolutionCost ? bondSize - resolutionCost : 0;
+        uint256 resolverRefund = resolutionCost * dataAvailabilityChallenge.resolverRefundPercentage() / 100;
+        resolverRefund = resolverRefund > resolutionCost ? resolutionCost : resolverRefund;
+        resolverRefund = resolverRefund > bondSize ? bondSize : resolverRefund;
+
+        if (challengerRefund > 0) {
+            vm.expectEmit(true, true, true, true);
+            emit BalanceChanged(challenger, challengerRefund);
+        }
+        if (resolverRefund > 0) {
+            vm.expectEmit(true, true, true, true);
+            emit BalanceChanged(resolver, resolverRefund);
+        }
+
         // Resolve the challenge
         vm.prank(resolver);
         dataAvailabilityChallenge.resolve(challengedBlockNumber, challengedCommitment, preImage);
@@ -274,27 +317,72 @@ contract DataAvailabilityChallengeTest is CommonTest {
             uint8(dataAvailabilityChallenge.getChallengeStatus(challengedBlockNumber, challengedCommitment)),
             uint8(ChallengeStatus.Resolved)
         );
-
-        // Assert challenger balance after bond distribution
-        uint256 resolutionCost = (
-            dataAvailabilityChallenge.fixedResolutionCost()
-                + preImage.length * dataAvailabilityChallenge.variableResolutionCost()
-                    / dataAvailabilityChallenge.variableResolutionCostPrecision()
-        ) * block.basefee;
-        uint256 challengerRefund = bondSize > resolutionCost ? bondSize - resolutionCost : 0;
-        assertEq(dataAvailabilityChallenge.balances(challenger), challengerRefund, "challenger refund");
-
-        // Assert resolver balance after bond distribution
-        uint256 resolverRefund = resolutionCost * dataAvailabilityChallenge.resolverRefundPercentage() / 100;
-        resolverRefund = resolverRefund > resolutionCost ? resolutionCost : resolverRefund;
-        resolverRefund = resolverRefund > bondSize ? bondSize : resolverRefund;
-        assertEq(dataAvailabilityChallenge.balances(resolver), resolverRefund, "resolver refund");
+        address _challenger = challenger;
+        address _resolver = resolver;
+        assertEq(dataAvailabilityChallenge.balances(_challenger), challengerRefund, "challenger refund");
+        assertEq(dataAvailabilityChallenge.balances(_resolver), resolverRefund, "resolver refund");
 
         // Assert burned amount after bond distribution
         uint256 burned = bondSize - challengerRefund - resolverRefund;
         assertEq(address(0).balance - zeroAddressBalanceBeforeResolve, burned, "burned bond");
     }
 
+    function test_resolve_invalidInputData_reverts(
+        address challenger,
+        address resolver,
+        bytes memory preImage,
+        bytes memory wrongPreImage,
+        uint256 challengedBlockNumber,
+        uint256 resolverRefundPercentage,
+        uint128 txGasPrice
+    )
+        public
+    {
+        // Assume neither the challenger nor resolver is address(0) and that they're not the same entity
+        vm.assume(challenger != address(0));
+        vm.assume(resolver != address(0));
+        vm.assume(challenger != resolver);
+        vm.assume(keccak256(preImage) != keccak256(wrongPreImage));
+
+        // Bound the resolver refund percentage to 100
+        resolverRefundPercentage = bound(resolverRefundPercentage, 0, 100);
+
+        // Set the gas price to a fuzzed value to test bond distribution logic
+        vm.txGasPrice(txGasPrice);
+
+        // Change the resolver refund percentage
+        vm.prank(dataAvailabilityChallenge.owner());
+        dataAvailabilityChallenge.setResolverRefundPercentage(resolverRefundPercentage);
+
+        // Assume the block number is not close to the max uint256 value
+        vm.assume(
+            challengedBlockNumber
+                < type(uint256).max - dataAvailabilityChallenge.challengeWindow()
+                    - dataAvailabilityChallenge.resolveWindow()
+        );
+        bytes memory challengedCommitment = computeCommitmentKeccak256(wrongPreImage);
+
+        // Move to block after challenged block
+        vm.roll(challengedBlockNumber + 1);
+
+        // Challenge the hash
+        uint256 bondSize = dataAvailabilityChallenge.bondSize();
+        vm.deal(challenger, bondSize);
+        vm.prank(challenger);
+        dataAvailabilityChallenge.challenge{ value: bondSize }(challengedBlockNumber, challengedCommitment);
+
+        // Resolve the challenge
+        vm.prank(resolver);
+        vm.expectRevert(
+            abi.encodeWithSelector(
+                IDataAvailabilityChallenge.InvalidInputData.selector,
+                computeCommitmentKeccak256(preImage),
+                challengedCommitment
+            )
+        );
+        dataAvailabilityChallenge.resolve(challengedBlockNumber, challengedCommitment, preImage);
+    }
+
     function test_resolve_nonExistentChallenge_reverts() public {
         bytes memory preImage = "some preimage";
         uint256 challengedBlockNumber = 1;
diff --git a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol
index 9b18479d5255..490ae07c927d 100644
--- a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol
+++ b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol
@@ -111,6 +111,10 @@ contract L2OutputOracle_getter_Test is L2OutputOracle_TestBase {
         // Querying with exact same block as proposed returns the proposal.
         uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1);
         assertEq(index1, 0);
+        assertEq(
+            keccak256(abi.encode(l2OutputOracle.getL2Output(index1))),
+            keccak256(abi.encode(output1, block.timestamp, nextBlockNumber1))
+        );
     }
 
     /// @dev Tests that `getL2OutputIndexAfter` returns the correct value
@@ -125,6 +129,10 @@ contract L2OutputOracle_getter_Test is L2OutputOracle_TestBase {
         // Querying with previous block returns the proposal too.
         uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1 - 1);
         assertEq(index1, 0);
+        assertEq(
+            keccak256(abi.encode(l2OutputOracle.getL2Output(index1))),
+            keccak256(abi.encode(output1, block.timestamp, nextBlockNumber1))
+        );
     }
 
     /// @dev Tests that `getL2OutputIndexAfter` returns the correct value.
@@ -156,14 +164,26 @@ contract L2OutputOracle_getter_Test is L2OutputOracle_TestBase {
         // Querying with a block number between the first and second proposal
         uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1 + 1);
         assertEq(index1, 1);
+        assertEq(
+            keccak256(abi.encode(l2OutputOracle.getL2Output(index1))),
+            keccak256(abi.encode(output2, l2OutputOracle.computeL2Timestamp(nextBlockNumber2) + 1, nextBlockNumber2))
+        );
 
         // Querying with a block number between the second and third proposal
         uint256 index2 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber2 + 1);
         assertEq(index2, 2);
+        assertEq(
+            keccak256(abi.encode(l2OutputOracle.getL2Output(index2))),
+            keccak256(abi.encode(output3, l2OutputOracle.computeL2Timestamp(nextBlockNumber3) + 1, nextBlockNumber3))
+        );
 
         // Querying with a block number between the third and fourth proposal
         uint256 index3 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber3 + 1);
         assertEq(index3, 3);
+        assertEq(
+            keccak256(abi.encode(l2OutputOracle.getL2Output(index3))),
+            keccak256(abi.encode(output4, l2OutputOracle.computeL2Timestamp(nextBlockNumber4) + 1, nextBlockNumber4))
+        );
     }
 
     /// @dev Tests that `getL2OutputIndexAfter` reverts when no output exists.
diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol
index 70b1c6d4ed94..f19e7ca6f5c2 100644
--- a/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol
+++ b/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol
@@ -598,7 +598,6 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
         // Get withdrawal proof data we can use for testing.
         (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) =
             ffi.getProveWithdrawalTransactionInputs(_defaultTx);
-
         // Setup a dummy output root proof for reuse.
         _outputRootProof = Types.OutputRootProof({
             version: bytes32(uint256(0)),
@@ -606,6 +605,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
             messagePasserStorageRoot: _storageRoot,
             latestBlockhash: bytes32(uint256(0))
         });
+
         _proposedBlockNumber = l2OutputOracle.nextBlockNumber();
         _proposedOutputIndex = l2OutputOracle.nextOutputIndex();
     }
@@ -934,7 +934,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
         assertEq(bobBalanceBefore, address(bob).balance);
     }
 
-    /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the target reverts.
+    /// @dev Tests that `finalizeWithdrawalTransaction` fails if the target reverts.
     function test_finalizeWithdrawalTransaction_targetFails_fails() external {
         uint256 bobBalanceBefore = address(bob).balance;
         vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
@@ -951,6 +951,77 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
         assert(address(bob).balance == bobBalanceBefore);
     }
 
+    /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the target reverts and caller is the
+    /// ESTIMATION_ADDRESS.
+    function test_finalizeWithdrawalTransaction_targetFailsAndCallerIsEstimationAddress_reverts() external {
+        vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
+
+        vm.expectEmit(true, true, true, true);
+        emit WithdrawalProven(_withdrawalHash, alice, bob);
+        optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
+
+        vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
+
+        vm.startPrank(Constants.ESTIMATION_ADDRESS, Constants.ESTIMATION_ADDRESS);
+        vm.expectRevert(GasEstimation.selector);
+        optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
+    }
+
+    /// @dev Tests that `finalizeWithdrawalTransaction` succeeds when _tx.data is empty.
+    function test_finalizeWithdrawalTransaction_noTxData_succeeds() external {
+        Types.WithdrawalTransaction memory _defaultTx_noData = Types.WithdrawalTransaction({
+            nonce: 0,
+            sender: alice,
+            target: bob,
+            value: 100,
+            gasLimit: 100_000,
+            data: hex""
+        });
+        // Get withdrawal proof data we can use for testing.
+        (
+            bytes32 _stateRoot_noData,
+            bytes32 _storageRoot_noData,
+            bytes32 _outputRoot_noData,
+            bytes32 _withdrawalHash_noData,
+            bytes[] memory _withdrawalProof_noData
+        ) = ffi.getProveWithdrawalTransactionInputs(_defaultTx_noData);
+        // Setup a dummy output root proof for reuse.
+        Types.OutputRootProof memory _outputRootProof_noData = Types.OutputRootProof({
+            version: bytes32(uint256(0)),
+            stateRoot: _stateRoot_noData,
+            messagePasserStorageRoot: _storageRoot_noData,
+            latestBlockhash: bytes32(uint256(0))
+        });
+
+        // Configure the oracle to return the output root we've prepared.
+        vm.mockCall(
+            address(l2OutputOracle),
+            abi.encodePacked(IL2OutputOracle.getL2Output.selector),
+            abi.encode(
+                Types.OutputProposal(
+                    _outputRoot_noData,
+                    l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp,
+                    uint128(_proposedBlockNumber)
+                )
+            )
+        );
+
+        uint256 bobBalanceBefore = address(bob).balance;
+
+        vm.expectEmit(true, true, true, true);
+        emit WithdrawalProven(_withdrawalHash_noData, alice, bob);
+        optimismPortal.proveWithdrawalTransaction(
+            _defaultTx_noData, _proposedOutputIndex, _outputRootProof_noData, _withdrawalProof_noData
+        );
+
+        vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
+        vm.expectEmit(true, true, false, true);
+        emit WithdrawalFinalized(_withdrawalHash_noData, true);
+        optimismPortal.finalizeWithdrawalTransaction(_defaultTx_noData);
+
+        assertEq(address(bob).balance, bobBalanceBefore + 100);
+    }
+
     /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the finalization period
     ///      has not yet passed.
     function test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() external {
diff --git a/packages/contracts-bedrock/test/setup/Events.sol b/packages/contracts-bedrock/test/setup/Events.sol
index 7f8017bfb2c1..966b236c30c8 100644
--- a/packages/contracts-bedrock/test/setup/Events.sol
+++ b/packages/contracts-bedrock/test/setup/Events.sol
@@ -104,4 +104,6 @@ contract Events {
     event Paused(string identifier);
 
     event Unpaused();
+
+    event BalanceChanged(address account, uint256 balance);
 }

From 3aac7289902b912a23241cb13b00ad920151611d Mon Sep 17 00:00:00 2001
From: protolambda 
Date: Wed, 13 Nov 2024 14:04:54 +0700
Subject: [PATCH 172/451] op-chain-ops: state forking, Go script forking,
 cheatcode access-control (#11919)

* op-chain-ops: state forking, Go script forking, cheatcode access-control

* add forking unit tests

* goimports

* linter

* fix state dump

* Add script test, hammer out bugs

* semgrep ignore scripts

* op-chain-ops: script state dump test

* fix merge error

* goimports

---------

Co-authored-by: Matthew Slipper 
---
 .semgrepignore                                |   3 +
 op-chain-ops/script/addresses/addresses.go    |  20 +
 op-chain-ops/script/cheatcodes.go             |  37 ++
 op-chain-ops/script/cheatcodes_environment.go |   4 +
 op-chain-ops/script/cheatcodes_forking.go     | 169 ++++++++
 op-chain-ops/script/console_test.go           |   6 +-
 op-chain-ops/script/context.go                |  23 +-
 op-chain-ops/script/fork.go                   |  60 +++
 op-chain-ops/script/forking/cache.go          | 103 +++++
 op-chain-ops/script/forking/cache_test.go     | 196 +++++++++
 op-chain-ops/script/forking/db.go             | 117 ++++++
 op-chain-ops/script/forking/diff.go           |  88 ++++
 op-chain-ops/script/forking/forking_test.go   | 291 ++++++++++++++
 op-chain-ops/script/forking/iface.go          |  52 +++
 op-chain-ops/script/forking/reader.go         |  36 ++
 op-chain-ops/script/forking/rpc.go            | 140 +++++++
 op-chain-ops/script/forking/rpc_test.go       | 210 ++++++++++
 op-chain-ops/script/forking/state.go          | 377 ++++++++++++++++++
 op-chain-ops/script/forking/trie.go           | 223 +++++++++++
 op-chain-ops/script/forking/trie_test.go      | 209 ++++++++++
 op-chain-ops/script/prank.go                  |   4 +-
 op-chain-ops/script/precompile.go             |   7 +
 op-chain-ops/script/script.go                 |  97 +++--
 op-chain-ops/script/script_test.go            | 212 +++++++++-
 .../testdata/scripts/ScriptExample.s.sol      |  52 +++
 .../ScriptExample.s.sol/FooBar.json           |   2 +-
 .../ScriptExample.s.sol/ForkTester.json       |   1 +
 .../ScriptExample.s.sol/ForkedContract.json   |   1 +
 .../ScriptExample.s.sol/NonceGetter.json      |   1 +
 .../ScriptExample.s.sol/ScriptExample.json    |   2 +-
 .../ScriptExample.s.sol/Vm.json               |   2 +-
 .../ScriptExample.s.sol/console.json          |   2 +-
 op-chain-ops/script/with.go                   |   8 +-
 33 files changed, 2684 insertions(+), 71 deletions(-)
 create mode 100644 op-chain-ops/script/addresses/addresses.go
 create mode 100644 op-chain-ops/script/cheatcodes_forking.go
 create mode 100644 op-chain-ops/script/fork.go
 create mode 100644 op-chain-ops/script/forking/cache.go
 create mode 100644 op-chain-ops/script/forking/cache_test.go
 create mode 100644 op-chain-ops/script/forking/db.go
 create mode 100644 op-chain-ops/script/forking/diff.go
 create mode 100644 op-chain-ops/script/forking/forking_test.go
 create mode 100644 op-chain-ops/script/forking/iface.go
 create mode 100644 op-chain-ops/script/forking/reader.go
 create mode 100644 op-chain-ops/script/forking/rpc.go
 create mode 100644 op-chain-ops/script/forking/rpc_test.go
 create mode 100644 op-chain-ops/script/forking/state.go
 create mode 100644 op-chain-ops/script/forking/trie.go
 create mode 100644 op-chain-ops/script/forking/trie_test.go
 create mode 100644 op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkTester.json
 create mode 100644 op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkedContract.json
 create mode 100644 op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/NonceGetter.json

diff --git a/.semgrepignore b/.semgrepignore
index b5fcb0303882..6c3e11fae449 100644
--- a/.semgrepignore
+++ b/.semgrepignore
@@ -13,3 +13,6 @@ vendor/
 
 # Semgrep-action log folder
 .semgrep_logs/
+
+# Test contracts the scripts folder
+op-chain-ops/script/testdata/scripts/
\ No newline at end of file
diff --git a/op-chain-ops/script/addresses/addresses.go b/op-chain-ops/script/addresses/addresses.go
new file mode 100644
index 000000000000..76895e203e37
--- /dev/null
+++ b/op-chain-ops/script/addresses/addresses.go
@@ -0,0 +1,20 @@
+package addresses
+
+import "github.com/ethereum/go-ethereum/common"
+
+var (
+	// DefaultSenderAddr is known as DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))))
+	DefaultSenderAddr = common.HexToAddress("0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38")
+	// DefaultScriptAddr is the address of the initial executing script, computed from:
+	// cast compute-address --nonce 1 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38
+	DefaultScriptAddr = common.HexToAddress("0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496")
+	// VMAddr is known as VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
+	VMAddr = common.HexToAddress("0x7109709ECfa91a80626fF3989D68f67F5b1DD12D")
+	// ConsoleAddr is known as CONSOLE, "console.log" in ascii.
+	// Utils like console.sol and console2.sol work by executing a staticcall to this address.
+	ConsoleAddr = common.HexToAddress("0x000000000000000000636F6e736F6c652e6c6f67")
+	// ScriptDeployer is used for temporary scripts address(uint160(uint256(keccak256("op-stack script deployer"))))
+	ScriptDeployer = common.HexToAddress("0x76Ce131128F3616871f8CDA86d18fAB44E4d0D8B")
+	// ForgeDeployer is used by some scripts as a default deployer address, e.g. makeAddr("deployer")
+	ForgeDeployer = common.HexToAddress("0xaE0bDc4eEAC5E950B67C6819B118761CaAF61946")
+)
diff --git a/op-chain-ops/script/cheatcodes.go b/op-chain-ops/script/cheatcodes.go
index 07755e527314..46e2bc4e314c 100644
--- a/op-chain-ops/script/cheatcodes.go
+++ b/op-chain-ops/script/cheatcodes.go
@@ -1,8 +1,45 @@
 package script
 
+import (
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/core/vm"
+)
+
 // CheatCodesPrecompile implements the Forge vm cheatcodes.
 // Note that forge-std wraps these cheatcodes,
 // and provides additional convenience functions that use these cheatcodes.
 type CheatCodesPrecompile struct {
 	h *Host
 }
+
+// AccessControlledPrecompile wraps a precompile,
+// and checks that the caller has cheatcode access.
+type AccessControlledPrecompile struct {
+	h     *Host
+	inner vm.PrecompiledContract
+}
+
+var _ vm.PrecompiledContract = (*AccessControlledPrecompile)(nil)
+
+func (c *AccessControlledPrecompile) RequiredGas(input []byte) uint64 {
+	// call-frame is not open yet, and prank is ignored for cheatcode access-checking.
+	accessor := c.h.SelfAddress()
+	_, ok := c.h.allowedCheatcodes[accessor]
+	if !ok {
+		// Don't just return infinite gas, we can allow it to run,
+		// and then revert with a proper error message.
+		return 0
+	}
+	return c.inner.RequiredGas(input)
+}
+
+func (c *AccessControlledPrecompile) Run(input []byte) ([]byte, error) {
+	// call-frame is not open yet, and prank is ignored for cheatcode access-checking.
+	accessor := c.h.SelfAddress()
+	if !c.h.AllowedCheatcodes(accessor) {
+		c.h.log.Error("Cheatcode access denied!", "caller", accessor, "label", c.h.labels[accessor])
+		return encodeRevert(fmt.Errorf("call by %s to cheatcode precompile is not allowed", accessor))
+	}
+	return c.inner.Run(input)
+}
diff --git a/op-chain-ops/script/cheatcodes_environment.go b/op-chain-ops/script/cheatcodes_environment.go
index 92baa429010e..55584c835db0 100644
--- a/op-chain-ops/script/cheatcodes_environment.go
+++ b/op-chain-ops/script/cheatcodes_environment.go
@@ -67,6 +67,10 @@ func (c *CheatCodesPrecompile) Load(account common.Address, slot [32]byte) [32]b
 // Etch implements https://book.getfoundry.sh/cheatcodes/etch
 func (c *CheatCodesPrecompile) Etch(who common.Address, code []byte) {
 	c.h.state.SetCode(who, bytes.Clone(code)) // important to clone; geth EVM will reuse the calldata memory.
+	if len(code) > 0 {
+		// if we're not just zeroing out the account: allow it to access cheatcodes
+		c.h.AllowCheatcodes(who)
+	}
 }
 
 // Deal implements https://book.getfoundry.sh/cheatcodes/deal
diff --git a/op-chain-ops/script/cheatcodes_forking.go b/op-chain-ops/script/cheatcodes_forking.go
new file mode 100644
index 000000000000..a1f30c7a11b9
--- /dev/null
+++ b/op-chain-ops/script/cheatcodes_forking.go
@@ -0,0 +1,169 @@
+package script
+
+import (
+	"errors"
+	"fmt"
+	"math/big"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
+)
+
+func (c *CheatCodesPrecompile) CreateFork_31ba3498(urlOrAlias string) (*big.Int, error) {
+	return c.createFork(ForkWithURLOrAlias(urlOrAlias))
+}
+
+func (c *CheatCodesPrecompile) CreateFork_6ba3ba2b(urlOrAlias string, block *big.Int) (*big.Int, error) {
+	return c.createFork(ForkWithURLOrAlias(urlOrAlias), ForkWithBlockNumberU256(block))
+}
+
+func (c *CheatCodesPrecompile) CreateFork_7ca29682(urlOrAlias string, txHash common.Hash) (*big.Int, error) {
+	return c.createFork(ForkWithURLOrAlias(urlOrAlias), ForkWithTransaction(txHash))
+}
+
+// createFork implements vm.createFork:
+// https://book.getfoundry.sh/cheatcodes/create-fork
+func (c *CheatCodesPrecompile) createFork(opts ...ForkOption) (*big.Int, error) {
+	src, err := c.h.onFork(opts...)
+	if err != nil {
+		return nil, fmt.Errorf("failed to setup fork source: %w", err)
+	}
+	id, err := c.h.state.CreateFork(src)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create fork: %w", err)
+	}
+	return id.U256().ToBig(), nil
+}
+
+func (c *CheatCodesPrecompile) CreateSelectFork_98680034(urlOrAlias string) (*big.Int, error) {
+	return c.createSelectFork(ForkWithURLOrAlias(urlOrAlias))
+}
+
+func (c *CheatCodesPrecompile) CreateSelectFork_71ee464d(urlOrAlias string, block *big.Int) (*big.Int, error) {
+	return c.createSelectFork(ForkWithURLOrAlias(urlOrAlias), ForkWithBlockNumberU256(block))
+}
+
+func (c *CheatCodesPrecompile) CreateSelectFork_84d52b7a(urlOrAlias string, txHash common.Hash) (*big.Int, error) {
+	return c.createSelectFork(ForkWithURLOrAlias(urlOrAlias), ForkWithTransaction(txHash))
+}
+
+// createSelectFork implements vm.createSelectFork:
+// https://book.getfoundry.sh/cheatcodes/create-select-fork
+func (c *CheatCodesPrecompile) createSelectFork(opts ...ForkOption) (*big.Int, error) {
+	src, err := c.h.onFork(opts...)
+	if err != nil {
+		return nil, fmt.Errorf("failed to setup fork source: %w", err)
+	}
+	id, err := c.h.state.CreateSelectFork(src)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create-select fork: %w", err)
+	}
+	return id.U256().ToBig(), nil
+}
+
+// ActiveFork implements vm.activeFork:
+// https://book.getfoundry.sh/cheatcodes/active-fork
+func (c *CheatCodesPrecompile) ActiveFork() (*uint256.Int, error) {
+	id, active := c.h.state.ActiveFork()
+	if !active {
+		return nil, errors.New("no active fork")
+	}
+	return id.U256(), nil
+}
+
+// convenience method, to repeat the same URLOrAlias as the given fork when setting up a new fork
+func (c *CheatCodesPrecompile) forkURLOption(id forking.ForkID) ForkOption {
+	return func(cfg *ForkConfig) error {
+		urlOrAlias, err := c.h.state.ForkURLOrAlias(id)
+		if err != nil {
+			return err
+		}
+		return ForkWithURLOrAlias(urlOrAlias)(cfg)
+	}
+}
+
+func (c *CheatCodesPrecompile) RollFork_d9bbf3a1(block *big.Int) error {
+	id, ok := c.h.state.ActiveFork()
+	if !ok {
+		return errors.New("no active fork")
+	}
+	return c.rollFork(id, c.forkURLOption(id), ForkWithBlockNumberU256(block))
+}
+
+func (c *CheatCodesPrecompile) RollFork_0f29772b(txHash common.Hash) error {
+	id, ok := c.h.state.ActiveFork()
+	if !ok {
+		return errors.New("no active fork")
+	}
+	return c.rollFork(id, c.forkURLOption(id), ForkWithTransaction(txHash))
+}
+
+func (c *CheatCodesPrecompile) RollFork_d74c83a4(forkID *big.Int, block *big.Int) error {
+	id := forking.ForkIDFromBig(forkID)
+	return c.rollFork(id, c.forkURLOption(id), ForkWithBlockNumberU256(block))
+}
+
+func (c *CheatCodesPrecompile) RollFork_f2830f7b(forkID *uint256.Int, txHash common.Hash) error {
+	id := forking.ForkID(*forkID)
+	return c.rollFork(id, c.forkURLOption(id), ForkWithTransaction(txHash))
+}
+
+// rollFork implements vm.rollFork:
+// https://book.getfoundry.sh/cheatcodes/roll-fork
+func (c *CheatCodesPrecompile) rollFork(id forking.ForkID, opts ...ForkOption) error {
+	src, err := c.h.onFork(opts...)
+	if err != nil {
+		return fmt.Errorf("cannot setup fork source for roll-fork change: %w", err)
+	}
+	return c.h.state.ResetFork(id, src)
+}
+
+// MakePersistent_57e22dde implements vm.makePersistent:
+// https://book.getfoundry.sh/cheatcodes/make-persistent
+func (c *CheatCodesPrecompile) MakePersistent_57e22dde(account0 common.Address) {
+	c.h.state.MakePersistent(account0)
+}
+
+func (c *CheatCodesPrecompile) MakePersistent_4074e0a8(account0, account1 common.Address) {
+	c.h.state.MakePersistent(account0)
+	c.h.state.MakePersistent(account1)
+}
+
+func (c *CheatCodesPrecompile) MakePersistent_efb77a75(account0, account1, account2 common.Address) {
+	c.h.state.MakePersistent(account0)
+	c.h.state.MakePersistent(account1)
+	c.h.state.MakePersistent(account2)
+}
+
+func (c *CheatCodesPrecompile) MakePersistent_1d9e269e(accounts []common.Address) {
+	for _, addr := range accounts {
+		c.h.state.MakePersistent(addr)
+	}
+}
+
+// RevokePersistent_997a0222 implements vm.revokePersistent:
+// https://book.getfoundry.sh/cheatcodes/revoke-persistent
+func (c *CheatCodesPrecompile) RevokePersistent_997a0222(addr common.Address) {
+	c.h.state.RevokePersistent(addr)
+}
+
+func (c *CheatCodesPrecompile) RevokePersistent_3ce969e6(addrs []common.Address) {
+	for _, addr := range addrs {
+		c.h.state.RevokePersistent(addr)
+	}
+}
+
+// IsPersistent implements vm.isPersistent:
+// https://book.getfoundry.sh/cheatcodes/is-persistent
+func (c *CheatCodesPrecompile) IsPersistent(addr common.Address) bool {
+	return c.h.state.IsPersistent(addr)
+}
+
+// AllowCheatcodes implements vm.allowCheatcodes:
+// https://book.getfoundry.sh/cheatcodes/allow-cheatcodes
+func (c *CheatCodesPrecompile) AllowCheatcodes(addr common.Address) {
+	c.h.AllowCheatcodes(addr)
+}
diff --git a/op-chain-ops/script/console_test.go b/op-chain-ops/script/console_test.go
index d67f5efc0f5f..70b78828b5d6 100644
--- a/op-chain-ops/script/console_test.go
+++ b/op-chain-ops/script/console_test.go
@@ -7,6 +7,8 @@ import (
 	"math/rand" // nosemgrep
 	"testing"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/log"
@@ -62,8 +64,8 @@ func TestFormatter(t *testing.T) {
 	require.Equal(t, "4.2", consoleFormat("%8e", big.NewInt(420000000)))
 	require.Equal(t, "foo true bar false", consoleFormat("foo %s bar %s", true, false))
 	require.Equal(t, "foo 1 bar 0", consoleFormat("foo %d bar %d", true, false))
-	require.Equal(t, "sender: "+DefaultSenderAddr.String(),
-		consoleFormat("sender: %s", DefaultSenderAddr))
+	require.Equal(t, "sender: "+addresses.DefaultSenderAddr.String(),
+		consoleFormat("sender: %s", addresses.DefaultSenderAddr))
 	require.Equal(t, "long 0.000000000000000042 number", consoleFormat("long %18e number", big.NewInt(42)))
 	require.Equal(t, "long 4200.000000000000000003 number", consoleFormat("long %18e number",
 		new(big.Int).Add(new(big.Int).Mul(
diff --git a/op-chain-ops/script/context.go b/op-chain-ops/script/context.go
index a6baad7cce66..fbb3704688e3 100644
--- a/op-chain-ops/script/context.go
+++ b/op-chain-ops/script/context.go
@@ -3,24 +3,9 @@ package script
 import (
 	"math/big"
 
-	"github.com/ethereum/go-ethereum/common"
-)
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
 
-var (
-	// DefaultSenderAddr is known as DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))))
-	DefaultSenderAddr = common.HexToAddress("0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38")
-	// DefaultScriptAddr is the address of the initial executing script, computed from:
-	// cast compute-address --nonce 1 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38
-	DefaultScriptAddr = common.HexToAddress("0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496")
-	// VMAddr is known as VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
-	VMAddr = common.HexToAddress("0x7109709ECfa91a80626fF3989D68f67F5b1DD12D")
-	// ConsoleAddr is known as CONSOLE, "console.log" in ascii.
-	// Utils like console.sol and console2.sol work by executing a staticcall to this address.
-	ConsoleAddr = common.HexToAddress("0x000000000000000000636F6e736F6c652e6c6f67")
-	// ScriptDeployer is used for temporary scripts address(uint160(uint256(keccak256("op-stack script deployer"))))
-	ScriptDeployer = common.HexToAddress("0x76Ce131128F3616871f8CDA86d18fAB44E4d0D8B")
-	// ForgeDeployer is used by some scripts as a default deployer address, e.g. makeAddr("deployer")
-	ForgeDeployer = common.HexToAddress("0xaE0bDc4eEAC5E950B67C6819B118761CaAF61946")
+	"github.com/ethereum/go-ethereum/common"
 )
 
 const (
@@ -42,8 +27,8 @@ type Context struct {
 
 var DefaultContext = Context{
 	ChainID:      big.NewInt(1337),
-	Sender:       DefaultSenderAddr,
-	Origin:       DefaultSenderAddr,
+	Sender:       addresses.DefaultSenderAddr,
+	Origin:       addresses.DefaultSenderAddr,
 	FeeRecipient: common.Address{},
 	GasLimit:     DefaultFoundryGasLimit,
 	BlockNum:     0,
diff --git a/op-chain-ops/script/fork.go b/op-chain-ops/script/fork.go
new file mode 100644
index 000000000000..8954a9984a35
--- /dev/null
+++ b/op-chain-ops/script/fork.go
@@ -0,0 +1,60 @@
+package script
+
+import (
+	"fmt"
+	"math/big"
+
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
+	"github.com/ethereum/go-ethereum/common"
+)
+
+// ForkOption modifies a ForkConfig, and can be used by Host internals,
+// like the forking cheatcodes, to customize the forking action.
+type ForkOption func(cfg *ForkConfig) error
+
+// ForkHook is a callback to the user of the Host,
+// to translate an intent to fork into a source of data that can be forked with.
+type ForkHook func(opts *ForkConfig) (forking.ForkSource, error)
+
+// ForkConfig is a bundle of data to express a fork intent
+type ForkConfig struct {
+	URLOrAlias  string
+	BlockNumber *uint64      // latest if nil
+	Transaction *common.Hash // up to pre-state of given transaction
+}
+
+func ForkWithURLOrAlias(urlOrAlias string) ForkOption {
+	return func(cfg *ForkConfig) error {
+		cfg.URLOrAlias = urlOrAlias
+		return nil
+	}
+}
+
+func ForkWithBlockNumberU256(num *big.Int) ForkOption {
+	return func(cfg *ForkConfig) error {
+		if !num.IsUint64() {
+			return fmt.Errorf("block number %s is too large", num.String())
+		}
+		v := num.Uint64()
+		cfg.BlockNumber = &v
+		return nil
+	}
+}
+
+func ForkWithTransaction(txHash common.Hash) ForkOption {
+	return func(cfg *ForkConfig) error {
+		cfg.Transaction = &txHash
+		return nil
+	}
+}
+
+// onFork is called by script-internals to translate a fork-intent into forks data-source.
+func (h *Host) onFork(opts ...ForkOption) (forking.ForkSource, error) {
+	cfg := &ForkConfig{}
+	for _, opt := range opts {
+		if err := opt(cfg); err != nil {
+			return nil, err
+		}
+	}
+	return h.hooks.OnFork(cfg)
+}
diff --git a/op-chain-ops/script/forking/cache.go b/op-chain-ops/script/forking/cache.go
new file mode 100644
index 000000000000..c9fec7bda96f
--- /dev/null
+++ b/op-chain-ops/script/forking/cache.go
@@ -0,0 +1,103 @@
+package forking
+
+import (
+	lru "github.com/hashicorp/golang-lru/v2"
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+type storageKey struct {
+	Addr common.Address
+	Slot common.Hash
+}
+
+// CachedSource wraps a ForkSource, and caches the retrieved data for faster repeat-queries.
+// The ForkSource should be immutable (as per the StateRoot value).
+// All cache data accumulates in-memory in LRU collections per data type.
+type CachedSource struct {
+	stateRoot common.Hash
+	src       ForkSource
+
+	nonces   *lru.Cache[common.Address, uint64]
+	balances *lru.Cache[common.Address, *uint256.Int]
+	storage  *lru.Cache[storageKey, common.Hash]
+	code     *lru.Cache[common.Address, []byte]
+}
+
+var _ ForkSource = (*CachedSource)(nil)
+
+func mustNewLRU[K comparable, V any](size int) *lru.Cache[K, V] {
+	out, err := lru.New[K, V](size)
+	if err != nil {
+		panic(err) // bad size parameter may produce an error
+	}
+	return out
+}
+
+func Cache(src ForkSource) *CachedSource {
+	return &CachedSource{
+		stateRoot: src.StateRoot(),
+		src:       src,
+		nonces:    mustNewLRU[common.Address, uint64](1000),
+		balances:  mustNewLRU[common.Address, *uint256.Int](1000),
+		storage:   mustNewLRU[storageKey, common.Hash](1000),
+		code:      mustNewLRU[common.Address, []byte](100),
+	}
+}
+
+func (c *CachedSource) URLOrAlias() string {
+	return c.src.URLOrAlias()
+}
+
+func (c *CachedSource) StateRoot() common.Hash {
+	return c.stateRoot
+}
+
+func (c *CachedSource) Nonce(addr common.Address) (uint64, error) {
+	if v, ok := c.nonces.Get(addr); ok {
+		return v, nil
+	}
+	v, err := c.src.Nonce(addr)
+	if err != nil {
+		return 0, err
+	}
+	c.nonces.Add(addr, v)
+	return v, nil
+}
+
+func (c *CachedSource) Balance(addr common.Address) (*uint256.Int, error) {
+	if v, ok := c.balances.Get(addr); ok {
+		return v.Clone(), nil
+	}
+	v, err := c.src.Balance(addr)
+	if err != nil {
+		return nil, err
+	}
+	c.balances.Add(addr, v)
+	return v.Clone(), nil
+}
+
+func (c *CachedSource) StorageAt(addr common.Address, key common.Hash) (common.Hash, error) {
+	if v, ok := c.storage.Get(storageKey{Addr: addr, Slot: key}); ok {
+		return v, nil
+	}
+	v, err := c.src.StorageAt(addr, key)
+	if err != nil {
+		return common.Hash{}, err
+	}
+	c.storage.Add(storageKey{Addr: addr, Slot: key}, v)
+	return v, nil
+}
+
+func (c *CachedSource) Code(addr common.Address) ([]byte, error) {
+	if v, ok := c.code.Get(addr); ok {
+		return v, nil
+	}
+	v, err := c.src.Code(addr)
+	if err != nil {
+		return nil, err
+	}
+	c.code.Add(addr, v)
+	return v, nil
+}
diff --git a/op-chain-ops/script/forking/cache_test.go b/op-chain-ops/script/forking/cache_test.go
new file mode 100644
index 000000000000..7ed4f9745042
--- /dev/null
+++ b/op-chain-ops/script/forking/cache_test.go
@@ -0,0 +1,196 @@
+package forking
+
+import (
+	"math/big"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/holiman/uint256"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+)
+
+// MockForkSource implements ForkSource interface for testing
+type MockForkSource struct {
+	mock.Mock
+}
+
+func (m *MockForkSource) URLOrAlias() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockForkSource) StateRoot() common.Hash {
+	args := m.Called()
+	return args.Get(0).(common.Hash)
+}
+
+func (m *MockForkSource) Nonce(addr common.Address) (uint64, error) {
+	args := m.Called(addr)
+	return args.Get(0).(uint64), args.Error(1)
+}
+
+func (m *MockForkSource) Balance(addr common.Address) (*uint256.Int, error) {
+	args := m.Called(addr)
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+	return args.Get(0).(*uint256.Int), args.Error(1)
+}
+
+func (m *MockForkSource) StorageAt(addr common.Address, key common.Hash) (common.Hash, error) {
+	args := m.Called(addr, key)
+	return args.Get(0).(common.Hash), args.Error(1)
+}
+
+func (m *MockForkSource) Code(addr common.Address) ([]byte, error) {
+	args := m.Called(addr)
+	return args.Get(0).([]byte), args.Error(1)
+}
+
+func setupCache(t *testing.T) (*CachedSource, *MockForkSource) {
+	mockSource := new(MockForkSource)
+	stateRoot := common.HexToHash("0x1234")
+	mockSource.On("StateRoot").Return(stateRoot)
+	mockSource.On("URLOrAlias").Return("test_source")
+
+	cached := Cache(mockSource)
+	require.NotNil(t, cached)
+	require.Equal(t, stateRoot, cached.StateRoot())
+	require.Equal(t, "test_source", cached.URLOrAlias())
+
+	return cached, mockSource
+}
+
+func TestCachedSource_Nonce(t *testing.T) {
+	cached, mockSource := setupCache(t)
+	addr := common.HexToAddress("0x1234")
+	expectedNonce := uint64(42)
+
+	// First call should hit the source
+	mockSource.On("Nonce", addr).Return(expectedNonce, nil).Once()
+
+	nonce, err := cached.Nonce(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedNonce, nonce)
+
+	// Second call should use cache
+	nonce, err = cached.Nonce(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedNonce, nonce)
+
+	mockSource.AssertNumberOfCalls(t, "Nonce", 1)
+}
+
+func TestCachedSource_Balance(t *testing.T) {
+	cached, mockSource := setupCache(t)
+	addr := common.HexToAddress("0x5678")
+	expectedBalance := uint256.NewInt(1000)
+
+	// First call should hit the source
+	mockSource.On("Balance", addr).Return(expectedBalance, nil).Once()
+
+	balance, err := cached.Balance(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedBalance, balance)
+
+	// Second call should use cache
+	balance, err = cached.Balance(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedBalance, balance)
+
+	// Verify the returned balance is a clone
+	balance.Add(balance, uint256.NewInt(1))
+	cachedBalance, _ := cached.Balance(addr)
+	require.Equal(t, expectedBalance, cachedBalance)
+
+	mockSource.AssertNumberOfCalls(t, "Balance", 1)
+}
+
+func TestCachedSource_Storage(t *testing.T) {
+	cached, mockSource := setupCache(t)
+	addr := common.HexToAddress("0x9abc")
+	slot := common.HexToHash("0xdef0")
+	expectedValue := common.HexToHash("0x1234")
+
+	// First call should hit the source
+	mockSource.On("StorageAt", addr, slot).Return(expectedValue, nil).Once()
+
+	value, err := cached.StorageAt(addr, slot)
+	require.NoError(t, err)
+	require.Equal(t, expectedValue, value)
+
+	// Second call should use cache
+	value, err = cached.StorageAt(addr, slot)
+	require.NoError(t, err)
+	require.Equal(t, expectedValue, value)
+
+	mockSource.AssertNumberOfCalls(t, "StorageAt", 1)
+}
+
+func TestCachedSource_Code(t *testing.T) {
+	cached, mockSource := setupCache(t)
+	addr := common.HexToAddress("0xdef0")
+	expectedCode := []byte{1, 2, 3, 4}
+
+	// First call should hit the source
+	mockSource.On("Code", addr).Return(expectedCode, nil).Once()
+
+	code, err := cached.Code(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedCode, code)
+
+	// Second call should use cache
+	code, err = cached.Code(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedCode, code)
+
+	mockSource.AssertNumberOfCalls(t, "Code", 1)
+}
+
+func TestCachedSource_CacheEviction(t *testing.T) {
+	cached, mockSource := setupCache(t)
+
+	// Test nonce cache eviction
+	for i := 0; i < 1001; i++ { // Cache size is 1000
+		addr := common.BigToAddress(big.NewInt(int64(i)))
+		mockSource.On("Nonce", addr).Return(uint64(i), nil).Once()
+		_, _ = cached.Nonce(addr)
+	}
+
+	// This should cause first address to be evicted
+	firstAddr := common.BytesToAddress([]byte{0})
+	mockSource.On("Nonce", firstAddr).Return(uint64(0), nil).Once()
+	_, _ = cached.Nonce(firstAddr)
+
+	mockSource.AssertNumberOfCalls(t, "Nonce", 1002) // 1001 + 1 for evicted key
+}
+
+func TestCachedSource_MultipleStorageSlots(t *testing.T) {
+	cached, mockSource := setupCache(t)
+	addr := common.HexToAddress("0xabcd")
+	slot1 := common.HexToHash("0x1111")
+	slot2 := common.HexToHash("0x2222")
+	value1 := common.HexToHash("0x3333")
+	value2 := common.HexToHash("0x4444")
+
+	mockSource.On("StorageAt", addr, slot1).Return(value1, nil).Once()
+	mockSource.On("StorageAt", addr, slot2).Return(value2, nil).Once()
+
+	// Different slots should trigger separate cache entries
+	val1, err := cached.StorageAt(addr, slot1)
+	require.NoError(t, err)
+	require.Equal(t, value1, val1)
+
+	val2, err := cached.StorageAt(addr, slot2)
+	require.NoError(t, err)
+	require.Equal(t, value2, val2)
+
+	// Verify both are cached
+	val1Again, _ := cached.StorageAt(addr, slot1)
+	val2Again, _ := cached.StorageAt(addr, slot2)
+	require.Equal(t, value1, val1Again)
+	require.Equal(t, value2, val2Again)
+
+	mockSource.AssertNumberOfCalls(t, "StorageAt", 2)
+}
diff --git a/op-chain-ops/script/forking/db.go b/op-chain-ops/script/forking/db.go
new file mode 100644
index 000000000000..6a40b4310d95
--- /dev/null
+++ b/op-chain-ops/script/forking/db.go
@@ -0,0 +1,117 @@
+package forking
+
+import (
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/core/rawdb"
+	"github.com/ethereum/go-ethereum/triedb/pathdb"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/state/snapshot"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/ethdb"
+	"github.com/ethereum/go-ethereum/trie/utils"
+	"github.com/ethereum/go-ethereum/triedb"
+)
+
+// ForkDB is a virtual state database: it wraps a forked accounts trie,
+// and can maintain a state diff, so we can mutate the forked state,
+// and even finalize state changes (so we can accurately measure things like cold storage gas cost).
+type ForkDB struct {
+	active *ForkedAccountsTrie
+}
+
+// Reader for read-only access to a known state. All cold reads go through this.
+// So the state-DB creates one initially, and then holds on to it.
+// The diff will be overlayed on the reader still. To get rid of the diff, it has to be explicitly cleared.
+// Warning: diffs applied to the original state that the reader wraps will be visible.
+// Geth StateDB is meant to be reinitialized after committing state.
+func (f *ForkDB) Reader(root common.Hash) (state.Reader, error) {
+	if root != f.active.stateRoot {
+		return nil, fmt.Errorf("current state is at %s, cannot open state at %s", f.active.stateRoot, root)
+	}
+	return &forkStateReader{
+		f.active,
+	}, nil
+}
+
+func (f *ForkDB) Snapshot() *snapshot.Tree {
+	return nil
+}
+
+var _ state.Database = (*ForkDB)(nil)
+
+func NewForkDB(source ForkSource) *ForkDB {
+	return &ForkDB{active: &ForkedAccountsTrie{
+		stateRoot: source.StateRoot(),
+		src:       source,
+		diff:      NewExportDiff(),
+	}}
+}
+
+// fakeRoot is just a marker; every account we load into the fork-db has this storage-root.
+// When opening a storage-trie, we sanity-check we have this root, or an empty trie.
+// And then just return the same global trie view for storage reads/writes.
+var fakeRoot = common.Hash{0: 42}
+
+func (f *ForkDB) OpenTrie(root common.Hash) (state.Trie, error) {
+	if f.active.stateRoot != root {
+		return nil, fmt.Errorf("active fork is at %s, but tried to open %s", f.active.stateRoot, root)
+	}
+	return f.active, nil
+}
+
+func (f *ForkDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie state.Trie) (state.Trie, error) {
+	if f.active.stateRoot != stateRoot {
+		return nil, fmt.Errorf("active fork is at %s, but tried to open account %s of state %s", f.active.stateRoot, address, stateRoot)
+	}
+	if _, ok := trie.(*ForkedAccountsTrie); !ok {
+		return nil, fmt.Errorf("ForkDB tried to open non-fork storage-trie %v", trie)
+	}
+	if root != fakeRoot && root != types.EmptyRootHash {
+		return nil, fmt.Errorf("ForkDB unexpectedly was queried with real looking storage root: %s", root)
+	}
+	return f.active, nil
+}
+
+func (f *ForkDB) CopyTrie(trie state.Trie) state.Trie {
+	if st, ok := trie.(*ForkedAccountsTrie); ok {
+		return st.Copy()
+	}
+	panic(fmt.Errorf("ForkDB tried to copy non-fork trie %v", trie))
+}
+
+func (f *ForkDB) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
+	return f.active.ContractCode(addr, codeHash)
+}
+
+func (f *ForkDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
+	return f.active.ContractCodeSize(addr, codeHash)
+}
+
+func (f *ForkDB) DiskDB() ethdb.KeyValueStore {
+	panic("DiskDB() during active Fork is not supported")
+}
+
+func (f *ForkDB) PointCache() *utils.PointCache {
+	panic("PointCache() is not supported")
+}
+
+func (f *ForkDB) TrieDB() *triedb.Database {
+	// The TrieDB is unused, but geth does use to check if Verkle is activated.
+	// So we have to create a read-only dummy one, to communicate that verkle really is disabled.
+	diskDB := rawdb.NewMemoryDatabase()
+	tdb := triedb.NewDatabase(diskDB, &triedb.Config{
+		Preimages: false,
+		IsVerkle:  false,
+		HashDB:    nil,
+		PathDB: &pathdb.Config{
+			StateHistory:   0,
+			CleanCacheSize: 0,
+			DirtyCacheSize: 0,
+			ReadOnly:       true,
+		},
+	})
+	return tdb
+}
diff --git a/op-chain-ops/script/forking/diff.go b/op-chain-ops/script/forking/diff.go
new file mode 100644
index 000000000000..c46b2704dc85
--- /dev/null
+++ b/op-chain-ops/script/forking/diff.go
@@ -0,0 +1,88 @@
+package forking
+
+import (
+	"bytes"
+	"maps"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+// AccountDiff represents changes to an account. Unchanged values of the account are not included.
+type AccountDiff struct {
+	// Nonce change.
+	// No diff if nil.
+	Nonce *uint64 `json:"nonce"`
+
+	// Balance change.
+	// No diff if nil.
+	Balance *uint256.Int `json:"balance"`
+
+	// Storage changes.
+	// No diff if not present in map. Deletions are zero-value entries.
+	Storage map[common.Hash]common.Hash `json:"storage"`
+
+	// CodeHash, for lookup of contract bytecode in the code diff map.
+	// No code-diff if nil.
+	CodeHash *common.Hash `json:"codeHash"`
+}
+
+func (d *AccountDiff) Copy() *AccountDiff {
+	var out AccountDiff
+	if d.Nonce != nil {
+		v := *d.Nonce // copy the value
+		out.Nonce = &v
+	}
+	if d.Balance != nil {
+		out.Balance = d.Balance.Clone()
+	}
+	if d.Storage != nil {
+		out.Storage = maps.Clone(d.Storage)
+	}
+	if d.CodeHash != nil {
+		h := *d.CodeHash
+		out.CodeHash = &h
+	}
+	return &out
+}
+
+type ExportDiff struct {
+	// Accounts diff. Deleted accounts are set to nil.
+	// Warning: this only contains finalized state changes.
+	// The state itself holds on to non-flushed changes.
+	Account map[common.Address]*AccountDiff `json:"account"`
+
+	// Stores new contract codes by code-hash
+	Code map[common.Hash][]byte `json:"code"`
+}
+
+func NewExportDiff() *ExportDiff {
+	return &ExportDiff{
+		Account: make(map[common.Address]*AccountDiff),
+		Code:    make(map[common.Hash][]byte),
+	}
+}
+
+func (ed *ExportDiff) Copy() *ExportDiff {
+	out := &ExportDiff{
+		Account: make(map[common.Address]*AccountDiff),
+		Code:    make(map[common.Hash][]byte),
+	}
+	for addr, acc := range ed.Account {
+		out.Account[addr] = acc.Copy()
+	}
+	for addr, code := range ed.Code {
+		out.Code[addr] = bytes.Clone(code)
+	}
+	return out
+}
+
+func (ed *ExportDiff) Any() bool {
+	return len(ed.Code) > 0 || len(ed.Account) > 0
+}
+
+func (ed *ExportDiff) Clear() {
+	ed.Account = make(map[common.Address]*AccountDiff)
+	ed.Code = make(map[common.Hash][]byte)
+}
diff --git a/op-chain-ops/script/forking/forking_test.go b/op-chain-ops/script/forking/forking_test.go
new file mode 100644
index 000000000000..e6e62c19fc19
--- /dev/null
+++ b/op-chain-ops/script/forking/forking_test.go
@@ -0,0 +1,291 @@
+package forking
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+	"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/crypto"
+	"github.com/ethereum/go-ethereum/triedb"
+	"github.com/ethereum/go-ethereum/triedb/hashdb"
+)
+
+type TestForkSource struct {
+	urlOrAlias string
+	stateRoot  common.Hash
+	nonces     map[common.Address]uint64
+	balances   map[common.Address]*uint256.Int
+	storage    map[common.Address]map[common.Hash]common.Hash
+	code       map[common.Address][]byte
+}
+
+func (t TestForkSource) URLOrAlias() string {
+	return t.urlOrAlias
+}
+
+func (t TestForkSource) StateRoot() common.Hash {
+	return t.stateRoot
+}
+
+func (t TestForkSource) Nonce(addr common.Address) (uint64, error) {
+	return t.nonces[addr], nil
+}
+
+func (t TestForkSource) Balance(addr common.Address) (*uint256.Int, error) {
+	b, ok := t.balances[addr]
+	if !ok {
+		return uint256.NewInt(0), nil
+	}
+	return b.Clone(), nil
+}
+
+func (t TestForkSource) StorageAt(addr common.Address, key common.Hash) (common.Hash, error) {
+	storage, ok := t.storage[addr]
+	if !ok {
+		return common.Hash{}, nil
+	}
+	return storage[key], nil
+}
+
+func (t TestForkSource) Code(addr common.Address) ([]byte, error) {
+	return t.code[addr], nil
+}
+
+var _ ForkSource = (*TestForkSource)(nil)
+
+func TestForking(t *testing.T) {
+	// create regular DB
+	rawDB := rawdb.NewMemoryDatabase()
+	stateDB := state.NewDatabase(triedb.NewDatabase(rawDB, &triedb.Config{
+		Preimages: true, // To be able to iterate the state we need the Preimages
+		IsVerkle:  false,
+		HashDB:    hashdb.Defaults,
+		PathDB:    nil,
+	}), nil)
+	baseState, err := state.New(types.EmptyRootHash, stateDB)
+	if err != nil {
+		panic(fmt.Errorf("failed to create memory state db: %w", err))
+	}
+	forkState := NewForkableState(baseState)
+
+	// No active fork yet
+	id, active := forkState.ActiveFork()
+	require.False(t, active)
+	require.Equal(t, ForkID{}, id)
+
+	name, err := forkState.ForkURLOrAlias(ForkID{})
+	require.ErrorContains(t, err, "default")
+	require.Equal(t, "", name)
+
+	alice := common.Address(bytes.Repeat([]byte{0xaa}, 20))
+	bob := common.Address(bytes.Repeat([]byte{0xbb}, 20))
+
+	forkState.CreateAccount(alice)
+	forkState.SetNonce(alice, 3)
+	forkState.AddBalance(alice, uint256.NewInt(123), tracing.BalanceChangeUnspecified)
+	// Check if writes worked
+	require.Equal(t, uint64(123), forkState.GetBalance(alice).Uint64())
+	require.Equal(t, uint64(3), forkState.GetNonce(alice))
+	// No active fork yet, balance change should be applied to underlying base-state
+	require.Equal(t, uint64(123), baseState.GetBalance(alice).Uint64())
+	require.Equal(t, uint64(3), baseState.GetNonce(alice))
+
+	src1 := &TestForkSource{
+		urlOrAlias: "src 1",
+		stateRoot:  crypto.Keccak256Hash([]byte("test fork state 1")),
+		nonces: map[common.Address]uint64{
+			alice: uint64(42),
+			bob:   uint64(1000),
+		},
+		balances: make(map[common.Address]*uint256.Int),
+		storage:  make(map[common.Address]map[common.Hash]common.Hash),
+		code:     make(map[common.Address][]byte),
+	}
+	forkA, err := forkState.CreateSelectFork(src1)
+	require.NoError(t, err)
+	// Check that we selected A
+	id, active = forkState.ActiveFork()
+	require.True(t, active)
+	require.Equal(t, forkA, id)
+	name, err = forkState.ForkURLOrAlias(forkA)
+	require.NoError(t, err)
+	require.Equal(t, "src 1", name)
+
+	// the fork has a different nonce for alice
+	require.Equal(t, uint64(42), forkState.GetNonce(alice))
+	// the fork has Bob, which didn't exist thus far
+	require.Equal(t, uint64(1000), forkState.GetNonce(bob))
+
+	// Apply a diff change on top of the fork
+	forkState.SetNonce(bob, 99999)
+
+	// Now unselect the fork, going back to the default again.
+	require.NoError(t, forkState.SelectFork(ForkID{}))
+	// No longer active fork
+	id, active = forkState.ActiveFork()
+	require.False(t, active)
+	require.Equal(t, ForkID{}, id)
+
+	// Check that things are back to normal
+	require.Equal(t, uint64(3), forkState.GetNonce(alice))
+	require.Equal(t, uint64(0), forkState.GetNonce(bob))
+
+	// Make a change to the base-state, to see if it survives going back to the fork.
+	forkState.SetNonce(bob, 5)
+
+	// Re-select the fork, see if the changes come back, including the diff we made
+	require.NoError(t, forkState.SelectFork(forkA))
+	require.Equal(t, uint64(42), forkState.GetNonce(alice))
+	require.Equal(t, uint64(99999), forkState.GetNonce(bob))
+
+	// This change will continue to be visible across forks,
+	// alice is going to be persistent.
+	forkState.SetNonce(alice, 777)
+
+	// Now make Alice persistent, see if we can get the original value
+	forkState.MakePersistent(alice)
+
+	// Activate a fork, to see if alice is really persistent
+	src2 := &TestForkSource{
+		urlOrAlias: "src 2",
+		stateRoot:  crypto.Keccak256Hash([]byte("test fork state 2")),
+		nonces: map[common.Address]uint64{
+			alice: uint64(2222),
+			bob:   uint64(222),
+		},
+		balances: make(map[common.Address]*uint256.Int),
+		storage:  make(map[common.Address]map[common.Hash]common.Hash),
+		code:     make(map[common.Address][]byte),
+	}
+	tmpFork, err := forkState.CreateSelectFork(src2)
+	require.NoError(t, err)
+	require.Equal(t, uint64(777), forkState.GetNonce(alice), "persistent original value")
+	// While bob is still read from the fork
+	require.Equal(t, uint64(222), forkState.GetNonce(bob), "bob is forked")
+
+	// Mutate both, and undo the fork, to test if the persistent change is still there in non-fork mode
+	forkState.SetNonce(alice, 1001) // this mutates forkA, because alice was made persistent there
+	forkState.SetNonce(bob, 1002)
+	require.NoError(t, forkState.SelectFork(ForkID{}))
+	require.Equal(t, uint64(1001), forkState.GetNonce(alice), "alice is persistent")
+	require.Equal(t, uint64(5), forkState.GetNonce(bob), "bob is not persistent")
+
+	// Stop alice persistence. Forks can now override it again.
+	forkState.RevokePersistent(alice)
+	// This foundry behavior is unspecified/undocumented.
+	// Not sure if correctly doing it by dropping the previously persisted state if it comes from another fork.
+	require.Equal(t, uint64(3), forkState.GetNonce(alice))
+	require.Equal(t, uint64(3), baseState.GetNonce(alice))
+	require.Equal(t, uint64(5), forkState.GetNonce(bob))
+
+	// Create another fork, don't select it immediately
+	src3 := &TestForkSource{
+		urlOrAlias: "src 3",
+		stateRoot:  crypto.Keccak256Hash([]byte("test fork state 3")),
+		nonces: map[common.Address]uint64{
+			alice: uint64(3333),
+		},
+		balances: make(map[common.Address]*uint256.Int),
+		storage:  make(map[common.Address]map[common.Hash]common.Hash),
+		code:     make(map[common.Address][]byte),
+	}
+	forkB, err := forkState.CreateFork(src3)
+	require.NoError(t, err)
+
+	id, active = forkState.ActiveFork()
+	require.False(t, active)
+	require.Equal(t, ForkID{}, id)
+
+	// forkA is still bound to src 1
+	name, err = forkState.ForkURLOrAlias(forkA)
+	require.NoError(t, err)
+	require.Equal(t, "src 1", name)
+	// tmpFork is still bound to src 2
+	name, err = forkState.ForkURLOrAlias(tmpFork)
+	require.NoError(t, err)
+	require.Equal(t, "src 2", name)
+	// forkB is on src 3
+	name, err = forkState.ForkURLOrAlias(forkB)
+	require.NoError(t, err)
+	require.Equal(t, "src 3", name)
+
+	require.Equal(t, uint64(3), forkState.GetNonce(alice), "not forked yet")
+	require.NoError(t, forkState.SelectFork(forkB))
+	id, active = forkState.ActiveFork()
+	require.True(t, active)
+	require.Equal(t, forkB, id)
+
+	// check if successfully forked now
+	require.Equal(t, uint64(3333), forkState.GetNonce(alice), "fork B active now")
+	// Bob is not in this fork. But that doesn't mean the base-state should be used.
+	require.Equal(t, uint64(0), forkState.GetNonce(bob))
+
+	// See if we can go from B straight to A
+	require.NoError(t, forkState.SelectFork(forkA))
+	require.Equal(t, uint64(1001), forkState.GetNonce(alice), "alice from A says hi")
+	// And back to B
+	require.NoError(t, forkState.SelectFork(forkB))
+	require.Equal(t, uint64(3333), forkState.GetNonce(alice), "alice from B says hi")
+
+	// And a fork on top of a fork; forks don't stack, they are their own individual contexts.
+	src4 := &TestForkSource{
+		urlOrAlias: "src 4",
+		stateRoot:  crypto.Keccak256Hash([]byte("test fork state 4")),
+		nonces: map[common.Address]uint64{
+			bob: uint64(9000),
+		},
+		balances: make(map[common.Address]*uint256.Int),
+		storage:  make(map[common.Address]map[common.Hash]common.Hash),
+		code:     make(map[common.Address][]byte),
+	}
+	forkC, err := forkState.CreateSelectFork(src4)
+	require.NoError(t, err)
+	// No alice in this fork.
+	require.Equal(t, uint64(0), forkState.GetNonce(alice))
+	// But bob is set
+	require.Equal(t, uint64(9000), forkState.GetNonce(bob))
+
+	// Put in some mutations, for the fork-diff testing
+	forkState.SetNonce(alice, 1234)
+	forkState.SetBalance(alice, uint256.NewInt(100_000), tracing.BalanceChangeUnspecified)
+	forkState.SetState(alice, common.Hash{4}, common.Hash{42})
+	forkState.SetState(alice, common.Hash{5}, common.Hash{100})
+	forkState.SetCode(alice, []byte("hello world"))
+
+	// Check the name
+	name, err = forkState.ForkURLOrAlias(forkC)
+	require.NoError(t, err)
+	require.Equal(t, "src 4", name)
+
+	// Now test our fork-diff exporting:
+	// it needs to reflect the changes we made to the fork, but not other fork contents.
+	forkADiff, err := forkState.ExportDiff(forkA)
+	require.NoError(t, err)
+	require.NotNil(t, forkADiff.Account[alice])
+	require.Equal(t, uint64(1001), *forkADiff.Account[alice].Nonce)
+	require.Equal(t, uint64(99999), *forkADiff.Account[bob].Nonce)
+
+	forkBDiff, err := forkState.ExportDiff(forkB)
+	require.NoError(t, err)
+	require.Len(t, forkBDiff.Account, 0, "no changes to fork B")
+
+	forkCDiff, err := forkState.ExportDiff(forkC)
+	require.NoError(t, err)
+	require.Contains(t, forkCDiff.Account, alice)
+	require.NotContains(t, forkCDiff.Account, bob)
+	require.Equal(t, uint64(1234), *forkCDiff.Account[alice].Nonce)
+	require.Equal(t, uint64(100_000), forkCDiff.Account[alice].Balance.Uint64())
+	require.Equal(t, common.Hash{42}, forkCDiff.Account[alice].Storage[common.Hash{4}])
+	require.Equal(t, common.Hash{100}, forkCDiff.Account[alice].Storage[common.Hash{5}])
+	require.Equal(t, crypto.Keccak256Hash([]byte("hello world")), *forkCDiff.Account[alice].CodeHash)
+	require.Equal(t, []byte("hello world"), forkCDiff.Code[*forkCDiff.Account[alice].CodeHash])
+}
diff --git a/op-chain-ops/script/forking/iface.go b/op-chain-ops/script/forking/iface.go
new file mode 100644
index 000000000000..2e1c29031be1
--- /dev/null
+++ b/op-chain-ops/script/forking/iface.go
@@ -0,0 +1,52 @@
+package forking
+
+import (
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/core/tracing"
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/vm"
+)
+
+type VMStateDB interface {
+	vm.StateDB
+	Finalise(deleteEmptyObjects bool)
+	// SetBalance sets the balance of an account. Not part of the geth VM StateDB interface (add/sub balance are).
+	SetBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason)
+}
+
+// ForkID is an identifier of a fork
+type ForkID uint256.Int
+
+func ForkIDFromBig(b *big.Int) ForkID {
+	return ForkID(*uint256.MustFromBig(b))
+}
+
+// U256 returns a uint256 copy of the fork ID, for usage inside the EVM.
+func (id *ForkID) U256() *uint256.Int {
+	return new(uint256.Int).Set((*uint256.Int)(id))
+}
+
+func (id ForkID) String() string {
+	return (*uint256.Int)(&id).String()
+}
+
+// ForkSource is a read-only source for ethereum state,
+// that can be used to fork a ForkableState.
+type ForkSource interface {
+	// URLOrAlias returns the URL or alias that the fork uses. This is not unique to a single fork.
+	URLOrAlias() string
+	// StateRoot returns the accounts-trie root of the committed-to state.
+	// This root must never change.
+	StateRoot() common.Hash
+	// Nonce returns 0, without error, if the account does not exist.
+	Nonce(addr common.Address) (uint64, error)
+	// Balance returns 0, without error, if the account does not exist.
+	Balance(addr common.Address) (*uint256.Int, error)
+	// StorageAt returns a zeroed hash, without error, if the storage does not exist.
+	StorageAt(addr common.Address, key common.Hash) (common.Hash, error)
+	// Code returns an empty byte slice, without error, if no code exists.
+	Code(addr common.Address) ([]byte, error)
+}
diff --git a/op-chain-ops/script/forking/reader.go b/op-chain-ops/script/forking/reader.go
new file mode 100644
index 000000000000..d943ddf14449
--- /dev/null
+++ b/op-chain-ops/script/forking/reader.go
@@ -0,0 +1,36 @@
+package forking
+
+import (
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
+)
+
+// forkStateReader implements the state.Reader abstraction,
+// for read-only access to a state-trie at a particular state-root.
+type forkStateReader struct {
+	trie *ForkedAccountsTrie
+}
+
+var _ state.Reader = (*forkStateReader)(nil)
+
+func (f *forkStateReader) Account(addr common.Address) (*types.StateAccount, error) {
+	acc, err := f.trie.GetAccount(addr)
+	if err != nil {
+		return nil, err
+	}
+	// We copy because the Reader interface defines that it should be safe to modify after returning.
+	return acc.Copy(), nil
+}
+
+func (f *forkStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
+	v, err := f.trie.GetStorage(addr, slot[:])
+	if err != nil {
+		return common.Hash{}, err
+	}
+	return common.Hash(v), nil
+}
+
+func (f *forkStateReader) Copy() state.Reader {
+	return f
+}
diff --git a/op-chain-ops/script/forking/rpc.go b/op-chain-ops/script/forking/rpc.go
new file mode 100644
index 000000000000..501c0fd6e372
--- /dev/null
+++ b/op-chain-ops/script/forking/rpc.go
@@ -0,0 +1,140 @@
+package forking
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+
+	"github.com/ethereum-optimism/optimism/op-service/retry"
+)
+
+type RPCClient interface {
+	CallContext(ctx context.Context, result any, method string, args ...any) error
+}
+
+type RPCSource struct {
+	stateRoot common.Hash
+	blockHash common.Hash
+
+	maxAttempts int
+	timeout     time.Duration
+	strategy    retry.Strategy
+
+	ctx    context.Context
+	cancel context.CancelFunc
+
+	client     RPCClient
+	urlOrAlias string
+}
+
+var _ ForkSource = (*RPCSource)(nil)
+
+func RPCSourceByNumber(urlOrAlias string, cl RPCClient, num uint64) (*RPCSource, error) {
+	src := newRPCSource(urlOrAlias, cl)
+	err := src.init(hexutil.Uint64(num))
+	return src, err
+}
+
+func RPCSourceByHash(urlOrAlias string, cl RPCClient, h common.Hash) (*RPCSource, error) {
+	src := newRPCSource(urlOrAlias, cl)
+	err := src.init(h)
+	return src, err
+}
+
+func newRPCSource(urlOrAlias string, cl RPCClient) *RPCSource {
+	ctx, cancel := context.WithCancel(context.Background())
+	return &RPCSource{
+		maxAttempts: 10,
+		timeout:     time.Second * 10,
+		strategy:    retry.Exponential(),
+		ctx:         ctx,
+		cancel:      cancel,
+		client:      cl,
+		urlOrAlias:  urlOrAlias,
+	}
+}
+
+type Header struct {
+	StateRoot common.Hash `json:"stateRoot"`
+	BlockHash common.Hash `json:"hash"`
+}
+
+func (r *RPCSource) init(id any) error {
+	head, err := retry.Do[*Header](r.ctx, r.maxAttempts, r.strategy, func() (*Header, error) {
+		var result *Header
+		err := r.client.CallContext(r.ctx, &result, "eth_getBlockByNumber", id, false)
+		if err == nil && result == nil {
+			err = ethereum.NotFound
+		}
+		return result, err
+	})
+	if err != nil {
+		return fmt.Errorf("failed to initialize RPC fork source around block %v: %w", id, err)
+	}
+	r.blockHash = head.BlockHash
+	r.stateRoot = head.StateRoot
+	return nil
+}
+
+func (c *RPCSource) URLOrAlias() string {
+	return c.urlOrAlias
+}
+
+func (r *RPCSource) BlockHash() common.Hash {
+	return r.blockHash
+}
+
+func (r *RPCSource) StateRoot() common.Hash {
+	return r.stateRoot
+}
+
+func (r *RPCSource) Nonce(addr common.Address) (uint64, error) {
+	return retry.Do[uint64](r.ctx, r.maxAttempts, r.strategy, func() (uint64, error) {
+		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
+		defer cancel()
+		var result hexutil.Uint64
+		err := r.client.CallContext(ctx, &result, "eth_getTransactionCount", addr, r.blockHash)
+		return uint64(result), err
+	})
+}
+
+func (r *RPCSource) Balance(addr common.Address) (*uint256.Int, error) {
+	return retry.Do[*uint256.Int](r.ctx, r.maxAttempts, r.strategy, func() (*uint256.Int, error) {
+		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
+		defer cancel()
+		var result hexutil.U256
+		err := r.client.CallContext(ctx, &result, "eth_getBalance", addr, r.blockHash)
+		return (*uint256.Int)(&result), err
+	})
+}
+
+func (r *RPCSource) StorageAt(addr common.Address, key common.Hash) (common.Hash, error) {
+	return retry.Do[common.Hash](r.ctx, r.maxAttempts, r.strategy, func() (common.Hash, error) {
+		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
+		defer cancel()
+		var result common.Hash
+		err := r.client.CallContext(ctx, &result, "eth_getStorageAt", addr, key, r.blockHash)
+		return result, err
+	})
+}
+
+func (r *RPCSource) Code(addr common.Address) ([]byte, error) {
+	return retry.Do[[]byte](r.ctx, r.maxAttempts, r.strategy, func() ([]byte, error) {
+		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
+		defer cancel()
+		var result hexutil.Bytes
+		err := r.client.CallContext(ctx, &result, "eth_getCode", addr, r.blockHash)
+		return result, err
+	})
+}
+
+// Close stops any ongoing RPC requests by cancelling the RPC context
+func (r *RPCSource) Close() {
+	r.cancel()
+}
diff --git a/op-chain-ops/script/forking/rpc_test.go b/op-chain-ops/script/forking/rpc_test.go
new file mode 100644
index 000000000000..9f23e957b1e9
--- /dev/null
+++ b/op-chain-ops/script/forking/rpc_test.go
@@ -0,0 +1,210 @@
+package forking
+
+import (
+	"context"
+	"errors"
+	"testing"
+	"time"
+
+	"github.com/ethereum-optimism/optimism/op-service/retry"
+
+	"github.com/ethereum/go-ethereum"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+	"github.com/holiman/uint256"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+)
+
+// MockRPCClient implements RPCClient interface for testing
+type MockRPCClient struct {
+	mock.Mock
+}
+
+func (m *MockRPCClient) CallContext(ctx context.Context, result any, method string, args ...any) error {
+	return m.Called(ctx, result, method, args).Error(0)
+}
+
+func TestRPCSourceInitialization(t *testing.T) {
+	mockClient := new(MockRPCClient)
+	expectedStateRoot := common.HexToHash("0x1234")
+	expectedBlockHash := common.HexToHash("0x5678")
+
+	t.Run("initialization by block number", func(t *testing.T) {
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("**forking.Header"),
+			"eth_getBlockByNumber", []any{hexutil.Uint64(123), false}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(**Header)
+				*result = &Header{
+					StateRoot: expectedStateRoot,
+					BlockHash: expectedBlockHash,
+				}
+			}).
+			Return(nil).Once()
+
+		source, err := RPCSourceByNumber("test_url", mockClient, 123)
+		require.NoError(t, err)
+		require.Equal(t, expectedStateRoot, source.StateRoot())
+		require.Equal(t, expectedBlockHash, source.BlockHash())
+	})
+
+	t.Run("initialization by block hash", func(t *testing.T) {
+		blockHash := common.HexToHash("0xabcd")
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("**forking.Header"),
+			"eth_getBlockByNumber", []any{blockHash, false}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(**Header)
+				*result = &Header{
+					StateRoot: expectedStateRoot,
+					BlockHash: expectedBlockHash,
+				}
+			}).
+			Return(nil).Once()
+
+		source, err := RPCSourceByHash("test_url", mockClient, blockHash)
+		require.NoError(t, err)
+		require.Equal(t, expectedStateRoot, source.StateRoot())
+		require.Equal(t, expectedBlockHash, source.BlockHash())
+	})
+
+	t.Run("initialization failure", func(t *testing.T) {
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("**forking.Header"),
+			"eth_getBlockByNumber", []any{hexutil.Uint64(999), false}).
+			Return(ethereum.NotFound).Times(2)
+
+		src := newRPCSource("test_url", mockClient)
+		strategy := retry.Exponential()
+		strategy.(*retry.ExponentialStrategy).Max = 100 * time.Millisecond
+		src.strategy = strategy
+		src.maxAttempts = 2
+		require.Error(t, src.init(hexutil.Uint64(999)))
+	})
+}
+
+func TestRPCSourceDataRetrieval(t *testing.T) {
+	mockClient := new(MockRPCClient)
+	testAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
+	blockHash := common.HexToHash("0xabcd")
+
+	source := &RPCSource{
+		blockHash:   blockHash,
+		client:      mockClient,
+		ctx:         context.Background(),
+		strategy:    retry.Exponential(),
+		maxAttempts: 10,
+		timeout:     time.Second * 10,
+	}
+
+	t.Run("get nonce", func(t *testing.T) {
+		expectedNonce := uint64(5)
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Uint64"),
+			"eth_getTransactionCount", []any{testAddr, blockHash}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(*hexutil.Uint64)
+				*result = hexutil.Uint64(expectedNonce)
+			}).
+			Return(nil).Once()
+
+		nonce, err := source.Nonce(testAddr)
+		require.NoError(t, err)
+		require.Equal(t, expectedNonce, nonce)
+	})
+
+	t.Run("get balance", func(t *testing.T) {
+		expectedBalance := uint256.NewInt(1000)
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.U256"),
+			"eth_getBalance", []any{testAddr, blockHash}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(*hexutil.U256)
+				*(*uint256.Int)(result) = *expectedBalance
+			}).
+			Return(nil).Once()
+
+		balance, err := source.Balance(testAddr)
+		require.NoError(t, err)
+		require.Equal(t, expectedBalance, balance)
+	})
+
+	t.Run("get storage", func(t *testing.T) {
+		storageKey := common.HexToHash("0x1234")
+		expectedValue := common.HexToHash("0x5678")
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*common.Hash"),
+			"eth_getStorageAt", []any{testAddr, storageKey, blockHash}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(*common.Hash)
+				*result = expectedValue
+			}).
+			Return(nil).Once()
+
+		value, err := source.StorageAt(testAddr, storageKey)
+		require.NoError(t, err)
+		require.Equal(t, expectedValue, value)
+	})
+
+	t.Run("get code", func(t *testing.T) {
+		expectedCode := []byte{1, 2, 3, 4}
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"),
+			"eth_getCode", []any{testAddr, blockHash}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(*hexutil.Bytes)
+				*result = expectedCode
+			}).
+			Return(nil).Once()
+
+		code, err := source.Code(testAddr)
+		require.NoError(t, err)
+		require.Equal(t, expectedCode, code)
+	})
+}
+
+func TestRPCSourceRetry(t *testing.T) {
+	mockClient := new(MockRPCClient)
+	testAddr := common.HexToAddress("0x1234")
+	blockHash := common.HexToHash("0xabcd")
+	strategy := retry.Exponential()
+	strategy.(*retry.ExponentialStrategy).Max = 100 * time.Millisecond
+
+	source := &RPCSource{
+		blockHash:   blockHash,
+		client:      mockClient,
+		ctx:         context.Background(),
+		strategy:    strategy,
+		maxAttempts: 3,
+		timeout:     time.Second * 10,
+	}
+
+	t.Run("retry on temporary error", func(t *testing.T) {
+		tempError := errors.New("temporary network error")
+
+		// Fail twice, succeed on third attempt
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Uint64"),
+			"eth_getTransactionCount", []any{testAddr, blockHash}).
+			Return(tempError).Times(2)
+
+		mockClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Uint64"),
+			"eth_getTransactionCount", []any{testAddr, blockHash}).
+			Run(func(args mock.Arguments) {
+				result := args.Get(1).(*hexutil.Uint64)
+				*result = hexutil.Uint64(5)
+			}).
+			Return(nil).Once()
+
+		nonce, err := source.Nonce(testAddr)
+		require.NoError(t, err)
+		require.Equal(t, uint64(5), nonce)
+	})
+}
+
+func TestRPCSourceClose(t *testing.T) {
+	mockClient := new(MockRPCClient)
+	source := newRPCSource("test_url", mockClient)
+
+	// Verify context is active before close
+	require.NoError(t, source.ctx.Err())
+
+	source.Close()
+
+	// Verify context is cancelled after close
+	require.Error(t, source.ctx.Err())
+	require.Equal(t, context.Canceled, source.ctx.Err())
+}
diff --git a/op-chain-ops/script/forking/state.go b/op-chain-ops/script/forking/state.go
new file mode 100644
index 000000000000..5370179423ab
--- /dev/null
+++ b/op-chain-ops/script/forking/state.go
@@ -0,0 +1,377 @@
+package forking
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/stateless"
+	"github.com/ethereum/go-ethereum/core/tracing"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/params"
+	"github.com/ethereum/go-ethereum/trie/utils"
+	"github.com/holiman/uint256"
+)
+
+type forkStateEntry struct {
+	state *state.StateDB
+}
+
+func (fe *forkStateEntry) DB() *ForkDB {
+	return fe.state.Database().(*ForkDB)
+}
+
+// ForkableState implements the vm.StateDB interface,
+// and a few other methods as defined in the VMStateDB interface.
+// This state can be forked in-place,
+// swapping over operations to route to in-memory states that wrap fork sources.
+type ForkableState struct {
+	selected VMStateDB
+
+	activeFork ForkID
+	forks      map[ForkID]*forkStateEntry
+
+	// persistent accounts will override any interactions
+	// to be directly with the forkID that was active at the time it was made persistent,
+	// rather than whatever fork is currently active.
+	persistent map[common.Address]ForkID
+
+	fallback VMStateDB
+
+	idCounter uint64
+}
+
+var _ VMStateDB = (*ForkableState)(nil)
+
+func NewForkableState(base VMStateDB) *ForkableState {
+	return &ForkableState{
+		selected:   base,
+		activeFork: ForkID{},
+		forks:      make(map[ForkID]*forkStateEntry),
+		persistent: map[common.Address]ForkID{
+			addresses.DefaultSenderAddr: ForkID{},
+			addresses.VMAddr:            ForkID{},
+			addresses.ConsoleAddr:       ForkID{},
+			addresses.ScriptDeployer:    ForkID{},
+			addresses.ForgeDeployer:     ForkID{},
+		},
+		fallback:  base,
+		idCounter: 0,
+	}
+}
+
+// ExportDiff exports a state diff. Warning: diffs are like flushed states.
+// So we flush the state, making all the contents cold, losing transient storage, etc.
+func (fst *ForkableState) ExportDiff(id ForkID) (*ExportDiff, error) {
+	if id == (ForkID{}) {
+		return nil, errors.New("default no-fork state does not have an exportable diff")
+	}
+	f, ok := fst.forks[id]
+	if !ok {
+		return nil, fmt.Errorf("unknown fork %q", id)
+	}
+	// Finalize the state content, so we can get an accurate diff.
+	f.state.IntermediateRoot(true)
+	tr := f.state.GetTrie()
+	ft, ok := tr.(*ForkedAccountsTrie)
+	if !ok {
+		return nil, fmt.Errorf("forked state trie is unexpectedly not a ForkedAccountsTrie: %T", tr)
+	}
+	diff := ft.ExportDiff()
+	// Now re-init the state, so we can use it again (albeit it cold).
+	forkDB := &ForkDB{active: ft}
+	st, err := state.New(forkDB.active.stateRoot, forkDB)
+	if err != nil {
+		return nil, fmt.Errorf("failed to construct fork state: %w", err)
+	}
+	fst.forks[id].state = st
+	if fst.activeFork == id {
+		fst.selected = st
+	}
+	return diff, nil
+}
+
+// CreateSelectFork is like vm.createSelectFork, it creates a fork, and selects it immediately.
+func (fst *ForkableState) CreateSelectFork(source ForkSource) (ForkID, error) {
+	id, err := fst.CreateFork(source)
+	if err != nil {
+		return id, err
+	}
+	return id, fst.SelectFork(id)
+}
+
+// CreateFork is like vm.createFork, it creates a fork, but does not select it yet.
+func (fst *ForkableState) CreateFork(source ForkSource) (ForkID, error) {
+	fst.idCounter += 1 // increment first, don't use ID 0
+	id := ForkID(*uint256.NewInt(fst.idCounter))
+	_, ok := fst.forks[id]
+	if ok { // sanity check our ID counter is consistent with the tracked forks
+		return id, fmt.Errorf("cannot create fork, fork %q already exists", id)
+	}
+	forkDB := NewForkDB(source)
+	st, err := state.New(forkDB.active.stateRoot, forkDB)
+	if err != nil {
+		return id, fmt.Errorf("failed to construct fork state: %w", err)
+	}
+	fst.forks[id] = &forkStateEntry{
+		state: st,
+	}
+	return id, nil
+}
+
+// SelectFork is like vm.selectFork, it activates the usage of a previously created fork.
+func (fst *ForkableState) SelectFork(id ForkID) error {
+	if id == (ForkID{}) {
+		fst.selected = fst.fallback
+		fst.activeFork = ForkID{}
+		return nil
+	}
+	f, ok := fst.forks[id]
+	if !ok {
+		return fmt.Errorf("cannot select fork, fork %q is unknown", id)
+	}
+	fst.selected = f.state
+	fst.activeFork = id
+	return nil
+}
+
+// ResetFork resets the fork to be coupled to the given fork-source.
+// Any ephemeral state changes (transient storage, warm s-loads, etc.)
+// as well as any uncommitted state, as well as any previously flushed diffs, will be lost.
+func (fst *ForkableState) ResetFork(id ForkID, src ForkSource) error {
+	if id == (ForkID{}) {
+		return errors.New("default no-fork state cannot change its ForkSource")
+	}
+	f, ok := fst.forks[id]
+	if !ok {
+		return fmt.Errorf("unknown fork %q", id)
+	}
+	// Now create a new state
+	forkDB := NewForkDB(src)
+	st, err := state.New(src.StateRoot(), forkDB)
+	if err != nil {
+		return fmt.Errorf("failed to construct fork state: %w", err)
+	}
+	f.state = st
+	if fst.activeFork == id {
+		fst.selected = st
+	}
+	return nil
+}
+
+// ActiveFork returns the ID current active fork, or active == false if no fork is active.
+func (fst *ForkableState) ActiveFork() (id ForkID, active bool) {
+	return fst.activeFork, fst.activeFork != (ForkID{})
+}
+
+// ForkURLOrAlias returns the URL or alias that the fork was configured with as source.
+// Returns an error if no fork is active
+func (fst *ForkableState) ForkURLOrAlias(id ForkID) (string, error) {
+	if id == (ForkID{}) {
+		return "", errors.New("default no-fork state does not have an URL or Alias")
+	}
+	f, ok := fst.forks[id]
+	if !ok {
+		return "", fmt.Errorf("unknown fork %q", id)
+	}
+	return f.DB().active.src.URLOrAlias(), nil
+}
+
+// SubstituteBaseState substitutes in a fallback state.
+func (fst *ForkableState) SubstituteBaseState(base VMStateDB) {
+	fst.fallback = base
+	// If the fallback is currently selected, also updated the fallback.
+	if fst.activeFork == (ForkID{}) {
+		fst.selected = base
+	}
+}
+
+// MakePersistent is like vm.makePersistent, it maintains this account context across all forks.
+// It does not make the account of a fork persistent, it makes an account override what might be in a fork.
+func (fst *ForkableState) MakePersistent(addr common.Address) {
+	fst.persistent[addr] = fst.activeFork
+}
+
+// RevokePersistent is like vm.revokePersistent, it undoes a previous vm.makePersistent.
+func (fst *ForkableState) RevokePersistent(addr common.Address) {
+	delete(fst.persistent, addr)
+}
+
+// IsPersistent is like vm.isPersistent, it checks if an account persists across forks.
+func (fst *ForkableState) IsPersistent(addr common.Address) bool {
+	_, ok := fst.persistent[addr]
+	return ok
+}
+
+func (fst *ForkableState) stateFor(addr common.Address) VMStateDB {
+	// if forked, check if we persisted this account across forks
+	persistedForkID, ok := fst.persistent[addr]
+	if ok {
+		if persistedForkID == (ForkID{}) {
+			return fst.fallback
+		}
+		return fst.forks[persistedForkID].state
+	}
+	// This may be the fallback state, if no fork is active.
+	return fst.selected
+}
+
+// Finalise finalises the state by removing the destructed objects and clears
+// the journal as well as the refunds. Finalise, however, will not push any updates
+// into the tries just yet.
+//
+// The changes will be flushed to the underlying DB.
+// A *ForkDB if the state is currently forked.
+func (fst *ForkableState) Finalise(deleteEmptyObjects bool) {
+	fst.selected.Finalise(deleteEmptyObjects)
+}
+
+func (fst *ForkableState) CreateAccount(address common.Address) {
+	fst.stateFor(address).CreateAccount(address)
+}
+
+func (fst *ForkableState) CreateContract(address common.Address) {
+	fst.stateFor(address).CreateContract(address)
+}
+
+func (fst *ForkableState) SubBalance(address common.Address, u *uint256.Int, reason tracing.BalanceChangeReason) {
+	fst.stateFor(address).SubBalance(address, u, reason)
+}
+
+func (fst *ForkableState) AddBalance(address common.Address, u *uint256.Int, reason tracing.BalanceChangeReason) {
+	fst.stateFor(address).AddBalance(address, u, reason)
+}
+
+func (fst *ForkableState) GetBalance(address common.Address) *uint256.Int {
+	return fst.stateFor(address).GetBalance(address)
+}
+
+func (fst *ForkableState) GetNonce(address common.Address) uint64 {
+	return fst.stateFor(address).GetNonce(address)
+}
+
+func (fst *ForkableState) SetNonce(address common.Address, u uint64) {
+	fst.stateFor(address).SetNonce(address, u)
+}
+
+func (fst *ForkableState) GetCodeHash(address common.Address) common.Hash {
+	return fst.stateFor(address).GetCodeHash(address)
+}
+
+func (fst *ForkableState) GetCode(address common.Address) []byte {
+	return fst.stateFor(address).GetCode(address)
+}
+
+func (fst *ForkableState) SetCode(address common.Address, bytes []byte) {
+	fst.stateFor(address).SetCode(address, bytes)
+}
+
+func (fst *ForkableState) GetCodeSize(address common.Address) int {
+	return fst.stateFor(address).GetCodeSize(address)
+}
+
+func (fst *ForkableState) AddRefund(u uint64) {
+	fst.selected.AddRefund(u)
+}
+
+func (fst *ForkableState) SubRefund(u uint64) {
+	fst.selected.SubRefund(u)
+}
+
+func (fst *ForkableState) GetRefund() uint64 {
+	return fst.selected.GetRefund()
+}
+
+func (fst *ForkableState) GetCommittedState(address common.Address, hash common.Hash) common.Hash {
+	return fst.stateFor(address).GetCommittedState(address, hash)
+}
+
+func (fst *ForkableState) GetState(address common.Address, k common.Hash) common.Hash {
+	return fst.stateFor(address).GetState(address, k)
+}
+
+func (fst *ForkableState) SetState(address common.Address, k common.Hash, v common.Hash) {
+	fst.stateFor(address).SetState(address, k, v)
+}
+
+func (fst *ForkableState) GetStorageRoot(addr common.Address) common.Hash {
+	return fst.stateFor(addr).GetStorageRoot(addr)
+}
+
+func (fst *ForkableState) GetTransientState(addr common.Address, key common.Hash) common.Hash {
+	return fst.stateFor(addr).GetTransientState(addr, key)
+}
+
+func (fst *ForkableState) SetTransientState(addr common.Address, key, value common.Hash) {
+	fst.stateFor(addr).SetTransientState(addr, key, value)
+}
+
+func (fst *ForkableState) SelfDestruct(address common.Address) {
+	fst.stateFor(address).SelfDestruct(address)
+}
+
+func (fst *ForkableState) HasSelfDestructed(address common.Address) bool {
+	return fst.stateFor(address).HasSelfDestructed(address)
+}
+
+func (fst *ForkableState) Selfdestruct6780(address common.Address) {
+	fst.stateFor(address).Selfdestruct6780(address)
+}
+
+func (fst *ForkableState) Exist(address common.Address) bool {
+	return fst.stateFor(address).Exist(address)
+}
+
+func (fst *ForkableState) Empty(address common.Address) bool {
+	return fst.stateFor(address).Empty(address)
+}
+
+func (fst *ForkableState) AddressInAccessList(addr common.Address) bool {
+	return fst.stateFor(addr).AddressInAccessList(addr)
+}
+
+func (fst *ForkableState) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
+	return fst.stateFor(addr).SlotInAccessList(addr, slot)
+}
+
+func (fst *ForkableState) AddAddressToAccessList(addr common.Address) {
+	fst.stateFor(addr).AddAddressToAccessList(addr)
+}
+
+func (fst *ForkableState) AddSlotToAccessList(addr common.Address, slot common.Hash) {
+	fst.stateFor(addr).AddSlotToAccessList(addr, slot)
+}
+
+func (fst *ForkableState) PointCache() *utils.PointCache {
+	return fst.selected.PointCache()
+}
+
+func (fst *ForkableState) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) {
+	fst.selected.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses)
+}
+
+func (fst *ForkableState) RevertToSnapshot(i int) {
+	fst.selected.RevertToSnapshot(i)
+}
+
+func (fst *ForkableState) Snapshot() int {
+	return fst.selected.Snapshot()
+}
+
+func (fst *ForkableState) AddLog(log *types.Log) {
+	fst.selected.AddLog(log)
+}
+
+func (fst *ForkableState) AddPreimage(hash common.Hash, img []byte) {
+	fst.selected.AddPreimage(hash, img)
+}
+
+func (fst *ForkableState) Witness() *stateless.Witness {
+	return fst.selected.Witness()
+}
+
+func (fst *ForkableState) SetBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
+	fst.stateFor(addr).SetBalance(addr, amount, reason)
+}
diff --git a/op-chain-ops/script/forking/trie.go b/op-chain-ops/script/forking/trie.go
new file mode 100644
index 000000000000..3af50bad973b
--- /dev/null
+++ b/op-chain-ops/script/forking/trie.go
@@ -0,0 +1,223 @@
+package forking
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/ethdb"
+	"github.com/ethereum/go-ethereum/trie"
+	"github.com/ethereum/go-ethereum/trie/trienode"
+)
+
+type ForkedAccountsTrie struct {
+	// stateRoot that this diff is based on top of
+	stateRoot common.Hash
+
+	// source to retrieve data from when it's not in the diff
+	src ForkSource
+
+	diff *ExportDiff
+}
+
+var _ state.Trie = (*ForkedAccountsTrie)(nil)
+
+func (f *ForkedAccountsTrie) Copy() *ForkedAccountsTrie {
+	return &ForkedAccountsTrie{
+		stateRoot: f.stateRoot,
+		diff:      f.diff.Copy(),
+	}
+}
+
+func (f *ForkedAccountsTrie) ExportDiff() *ExportDiff {
+	return f.diff.Copy()
+}
+
+func (f *ForkedAccountsTrie) HasDiff() bool {
+	return len(f.diff.Code) > 0 || len(f.diff.Account) > 0
+}
+
+// ClearDiff clears the flushed changes. This does not clear the warm state changes.
+// To fully clear, first Finalise the forked state that uses this trie, and then clear the diff.
+func (f *ForkedAccountsTrie) ClearDiff() {
+	f.diff.Clear()
+}
+
+// ContractCode is not directly part of the vm.State interface,
+// but is used by the ForkDB to retrieve the contract code.
+func (f *ForkedAccountsTrie) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
+	diffAcc, ok := f.diff.Account[addr]
+	if ok {
+		if diffAcc.CodeHash != nil && *diffAcc.CodeHash != codeHash {
+			return nil, fmt.Errorf("account code changed to %s, cannot get code %s of account %s", *diffAcc.CodeHash, codeHash, addr)
+		}
+		if code, ok := f.diff.Code[codeHash]; ok {
+			return code, nil
+		}
+		// if not in codeDiff, the actual code has not changed.
+	}
+	code, err := f.src.Code(addr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve code: %w", err)
+	}
+	// sanity-check the retrieved code matches the expected codehash
+	if h := crypto.Keccak256Hash(code); h != codeHash {
+		return nil, fmt.Errorf("retrieved code of %s hashed to %s, but expected %s", addr, h, codeHash)
+	}
+	return code, nil
+}
+
+// ContractCodeSize is not directly part of the vm.State interface,
+// but is used by the ForkDB to retrieve the contract code-size.
+func (f *ForkedAccountsTrie) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
+	code, err := f.ContractCode(addr, codeHash)
+	if err != nil {
+		return 0, fmt.Errorf("cannot get contract code to determine code size: %w", err)
+	}
+	return len(code), nil
+}
+
+func (f *ForkedAccountsTrie) GetKey(bytes []byte) []byte {
+	panic("arbitrary key lookups on ForkedAccountsTrie are not supported")
+}
+
+func (f *ForkedAccountsTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
+	acc := &types.StateAccount{
+		Nonce:    0,
+		Balance:  nil,
+		Root:     fakeRoot,
+		CodeHash: nil,
+	}
+	diffAcc := f.diff.Account[address]
+	if diffAcc != nil && diffAcc.Nonce != nil {
+		acc.Nonce = *diffAcc.Nonce
+	} else {
+		v, err := f.src.Nonce(address)
+		if err != nil {
+			return nil, fmt.Errorf("failed to retrieve nonce of account %s: %w", address, err)
+		}
+		acc.Nonce = v
+	}
+	if diffAcc != nil && diffAcc.Balance != nil {
+		acc.Balance = new(uint256.Int).Set(diffAcc.Balance)
+	} else {
+		v, err := f.src.Balance(address)
+		if err != nil {
+			return nil, fmt.Errorf("failed to retrieve balance of account %s: %w", address, err)
+		}
+		acc.Balance = new(uint256.Int).Set(v)
+	}
+	if diffAcc != nil && diffAcc.CodeHash != nil {
+		cpy := *diffAcc.CodeHash
+		acc.CodeHash = cpy.Bytes()
+	} else {
+		v, err := f.src.Code(address)
+		if err != nil {
+			return nil, fmt.Errorf("failed to retrieve code of account %s: %w", address, err)
+		}
+		acc.CodeHash = crypto.Keccak256Hash(v).Bytes()
+	}
+	return acc, nil
+}
+
+func (f *ForkedAccountsTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+	k := common.BytesToHash(key)
+	diffAcc, ok := f.diff.Account[addr]
+	if ok { // if there is a diff, try and see if it contains a storage diff
+		v, ok := diffAcc.Storage[k]
+		if ok { // if the storage has changed, return that change
+			return v.Bytes(), nil
+		}
+	}
+	v, err := f.src.StorageAt(addr, k)
+	if err != nil {
+		return nil, err
+	}
+	return v.Bytes(), nil
+}
+
+func (f *ForkedAccountsTrie) UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error {
+	// Ignored, account contains the code details we need.
+	// Also see the trie.StateTrie of geth itself, which ignores this arg too.
+	_ = codeLen
+
+	nonce := account.Nonce
+	b := account.Balance.Clone()
+	codeHash := common.BytesToHash(account.CodeHash)
+	out := &AccountDiff{
+		Nonce:    &nonce,
+		Balance:  b,
+		Storage:  nil,
+		CodeHash: &codeHash,
+	}
+	// preserve the storage diff
+	if diffAcc, ok := f.diff.Account[address]; ok {
+		out.Storage = diffAcc.Storage
+	}
+	f.diff.Account[address] = out
+	return nil
+}
+
+func (f *ForkedAccountsTrie) UpdateStorage(addr common.Address, key, value []byte) error {
+	diffAcc, ok := f.diff.Account[addr]
+	if !ok {
+		diffAcc = &AccountDiff{}
+		f.diff.Account[addr] = diffAcc
+	}
+	if diffAcc.Storage == nil {
+		diffAcc.Storage = make(map[common.Hash]common.Hash)
+	}
+	k := common.BytesToHash(key)
+	v := common.BytesToHash(value)
+	diffAcc.Storage[k] = v
+	return nil
+}
+
+func (f *ForkedAccountsTrie) DeleteAccount(address common.Address) error {
+	f.diff.Account[address] = nil
+	return nil
+}
+
+func (f *ForkedAccountsTrie) DeleteStorage(addr common.Address, key []byte) error {
+	return f.UpdateStorage(addr, key, nil)
+}
+
+func (f *ForkedAccountsTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
+	diffAcc, ok := f.diff.Account[addr]
+	if !ok {
+		diffAcc = &AccountDiff{}
+		f.diff.Account[addr] = diffAcc
+	}
+	diffAcc.CodeHash = &codeHash
+	f.diff.Code[codeHash] = code
+	return nil
+}
+
+func (f *ForkedAccountsTrie) Hash() common.Hash {
+	return f.stateRoot
+}
+
+func (f *ForkedAccountsTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) {
+	panic("cannot commit state-changes of a forked trie")
+}
+
+func (f *ForkedAccountsTrie) Witness() map[string]struct{} {
+	panic("witness generation of a ForkedAccountsTrie is not supported")
+}
+
+func (f *ForkedAccountsTrie) NodeIterator(startKey []byte) (trie.NodeIterator, error) {
+	return nil, errors.New("node iteration of a ForkedAccountsTrie is not supported")
+}
+
+func (f *ForkedAccountsTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
+	return errors.New("proving of a ForkedAccountsTrie is not supported")
+}
+
+func (f *ForkedAccountsTrie) IsVerkle() bool {
+	return false
+}
diff --git a/op-chain-ops/script/forking/trie_test.go b/op-chain-ops/script/forking/trie_test.go
new file mode 100644
index 000000000000..6d703814ed4d
--- /dev/null
+++ b/op-chain-ops/script/forking/trie_test.go
@@ -0,0 +1,209 @@
+package forking
+
+import (
+	"testing"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/holiman/uint256"
+	"github.com/stretchr/testify/require"
+)
+
+func setupTrie(t *testing.T) (*ForkedAccountsTrie, *MockForkSource) {
+	mockSource := new(MockForkSource)
+	stateRoot := common.HexToHash("0x1234")
+	mockSource.On("StateRoot").Return(stateRoot)
+
+	trie := &ForkedAccountsTrie{
+		stateRoot: stateRoot,
+		src:       mockSource,
+		diff:      NewExportDiff(),
+	}
+	return trie, mockSource
+}
+
+func TestForkedAccountsTrie_GetAccount(t *testing.T) {
+	trie, mockSource := setupTrie(t)
+	addr := common.HexToAddress("0x1234")
+
+	// Setup mock responses
+	expectedNonce := uint64(1)
+	expectedBalance := uint256.NewInt(100)
+	expectedCode := []byte{1, 2, 3, 4}
+	expectedCodeHash := crypto.Keccak256Hash(expectedCode)
+
+	mockSource.On("Nonce", addr).Return(expectedNonce, nil)
+	mockSource.On("Balance", addr).Return(expectedBalance, nil)
+	mockSource.On("Code", addr).Return(expectedCode, nil)
+
+	// Test initial account retrieval
+	account, err := trie.GetAccount(addr)
+	require.NoError(t, err)
+	require.Equal(t, expectedNonce, account.Nonce)
+	require.Equal(t, expectedBalance, uint256.NewInt(0).SetBytes(account.Balance.Bytes()))
+	require.Equal(t, expectedCodeHash.Bytes(), account.CodeHash)
+
+	// Update account and verify diff
+	newNonce := uint64(2)
+	newBalance := uint256.NewInt(200)
+	account.Nonce = newNonce
+	account.Balance = newBalance
+
+	err = trie.UpdateAccount(addr, account, 0)
+	require.NoError(t, err)
+
+	// Verify updated account
+	updatedAccount, err := trie.GetAccount(addr)
+	require.NoError(t, err)
+	require.Equal(t, newNonce, updatedAccount.Nonce)
+	require.Equal(t, newBalance, uint256.NewInt(0).SetBytes(updatedAccount.Balance.Bytes()))
+}
+
+func TestForkedAccountsTrie_Storage(t *testing.T) {
+	trie, mockSource := setupTrie(t)
+	addr := common.HexToAddress("0x1234")
+	key := common.HexToHash("0x1")
+	value := common.HexToHash("0x2")
+
+	// Setup mock for initial storage value
+	mockSource.On("StorageAt", addr, key).Return(value, nil)
+
+	// Test initial storage retrieval
+	storageValue, err := trie.GetStorage(addr, key.Bytes())
+	require.NoError(t, err)
+	require.Equal(t, value.Bytes(), storageValue)
+
+	// Update storage
+	newValue := common.HexToHash("0x3")
+	err = trie.UpdateStorage(addr, key.Bytes(), newValue.Bytes())
+	require.NoError(t, err)
+
+	// Verify updated storage
+	updatedValue, err := trie.GetStorage(addr, key.Bytes())
+	require.NoError(t, err)
+	require.Equal(t, newValue.Bytes(), updatedValue)
+}
+
+func TestForkedAccountsTrie_ContractCode(t *testing.T) {
+	trie, mockSource := setupTrie(t)
+	addr := common.HexToAddress("0x1234")
+	code := []byte{1, 2, 3, 4}
+	codeHash := crypto.Keccak256Hash(code)
+
+	// Setup mock for code retrieval
+	mockSource.On("Code", addr).Return(code, nil)
+
+	// Test initial code retrieval
+	retrievedCode, err := trie.ContractCode(addr, codeHash)
+	require.NoError(t, err)
+	require.Equal(t, code, retrievedCode)
+
+	// Update code
+	newCode := []byte{5, 6, 7, 8}
+	newCodeHash := crypto.Keccak256Hash(newCode)
+
+	err = trie.UpdateContractCode(addr, newCodeHash, newCode)
+	require.NoError(t, err)
+
+	// Verify updated code
+	updatedCode, err := trie.ContractCode(addr, newCodeHash)
+	require.NoError(t, err)
+	require.Equal(t, newCode, updatedCode)
+}
+
+func TestForkedAccountsTrie_DeleteAccount(t *testing.T) {
+	trie, _ := setupTrie(t)
+	addr := common.HexToAddress("0x1234")
+
+	// Setup initial account
+	account := &types.StateAccount{
+		Nonce:    1,
+		Balance:  uint256.NewInt(100),
+		CodeHash: crypto.Keccak256([]byte{1, 2, 3, 4}),
+	}
+
+	err := trie.UpdateAccount(addr, account, 0)
+	require.NoError(t, err)
+
+	// Delete account
+	err = trie.DeleteAccount(addr)
+	require.NoError(t, err)
+
+	// Verify account is marked as deleted in diff
+	require.Nil(t, trie.diff.Account[addr])
+}
+
+func TestForkedAccountsTrie_Copy(t *testing.T) {
+	trie, _ := setupTrie(t)
+	addr := common.HexToAddress("0x1234")
+
+	// Setup some initial state
+	account := &types.StateAccount{
+		Nonce:    1,
+		Balance:  uint256.NewInt(100),
+		CodeHash: crypto.Keccak256([]byte{1, 2, 3, 4}),
+	}
+	err := trie.UpdateAccount(addr, account, 0)
+	require.NoError(t, err)
+
+	// Make a copy
+	cpy := trie.Copy()
+
+	// Verify copy has same state
+	require.Equal(t, trie.stateRoot, cpy.stateRoot)
+	require.Equal(t, trie.diff.Account[addr].Nonce, cpy.diff.Account[addr].Nonce)
+	require.True(t, trie.diff.Account[addr].Balance.Eq(cpy.diff.Account[addr].Balance))
+
+	// Modify copy and verify original is unchanged
+	newAccount := &types.StateAccount{
+		Nonce:    2,
+		Balance:  uint256.NewInt(200),
+		CodeHash: crypto.Keccak256([]byte{5, 6, 7, 8}),
+	}
+	err = cpy.UpdateAccount(addr, newAccount, 0)
+	require.NoError(t, err)
+
+	originalAccount, err := trie.GetAccount(addr)
+	require.NoError(t, err)
+	require.Equal(t, uint64(1), originalAccount.Nonce)
+	require.True(t, uint256.NewInt(100).Eq(uint256.NewInt(0).SetBytes(originalAccount.Balance.Bytes())))
+}
+
+func TestForkedAccountsTrie_HasDiff(t *testing.T) {
+	trie, _ := setupTrie(t)
+
+	// Initially no diff
+	require.False(t, trie.HasDiff())
+
+	// Add account change
+	addr := common.HexToAddress("0x1234")
+	account := &types.StateAccount{
+		Nonce:    1,
+		Balance:  uint256.NewInt(100),
+		CodeHash: crypto.Keccak256([]byte{1, 2, 3, 4}),
+	}
+	err := trie.UpdateAccount(addr, account, 0)
+	require.NoError(t, err)
+
+	// Verify diff exists
+	require.True(t, trie.HasDiff())
+
+	// Clear diff
+	trie.ClearDiff()
+	require.False(t, trie.HasDiff())
+}
+
+func TestForkedAccountsTrie_UnsupportedOperations(t *testing.T) {
+	trie, _ := setupTrie(t)
+
+	require.Panics(t, func() { trie.GetKey([]byte{1, 2, 3}) })
+	require.Panics(t, func() { trie.Commit(false) })
+	require.Panics(t, func() { trie.Witness() })
+
+	_, err := trie.NodeIterator(nil)
+	require.Error(t, err)
+
+	err = trie.Prove(nil, nil)
+	require.Error(t, err)
+}
diff --git a/op-chain-ops/script/prank.go b/op-chain-ops/script/prank.go
index b6a68f4c44b1..cdea026063a2 100644
--- a/op-chain-ops/script/prank.go
+++ b/op-chain-ops/script/prank.go
@@ -8,6 +8,8 @@ import (
 	"fmt"
 	"math/big"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+
 	"github.com/holiman/uint256"
 
 	"github.com/ethereum/go-ethereum/common"
@@ -53,7 +55,7 @@ func (h *Host) handleCaller(caller vm.ContractRef) vm.ContractRef {
 	// apply prank, if top call-frame had set up a prank
 	if len(h.callStack) > 0 {
 		parentCallFrame := h.callStack[len(h.callStack)-1]
-		if parentCallFrame.Prank != nil && caller.Address() != VMAddr { // pranks do not apply to the cheatcode precompile
+		if parentCallFrame.Prank != nil && caller.Address() != addresses.VMAddr { // pranks do not apply to the cheatcode precompile
 			if parentCallFrame.Prank.Broadcast && parentCallFrame.LastOp == vm.CREATE2 && h.useCreate2Deployer {
 				return &prankRef{
 					prank: DeterministicDeployerAddress,
diff --git a/op-chain-ops/script/precompile.go b/op-chain-ops/script/precompile.go
index 2dcea6601883..0ac692be7c1b 100644
--- a/op-chain-ops/script/precompile.go
+++ b/op-chain-ops/script/precompile.go
@@ -9,6 +9,8 @@ import (
 	"reflect"
 	"strings"
 
+	"github.com/holiman/uint256"
+
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/hexutil"
@@ -352,6 +354,8 @@ type ABIInt256 big.Int
 
 var abiInt256Type = typeFor[ABIInt256]()
 
+var abiUint256Type = typeFor[uint256.Int]()
+
 // goTypeToSolidityType converts a Go type to the solidity ABI type definition.
 // The "internalType" is a quirk of the Geth ABI utils, for nested structures.
 // Unfortunately we have to convert to string, not directly to ABI type structure,
@@ -364,6 +368,9 @@ func goTypeToSolidityType(typ reflect.Type) (typeDef, internalType string, err e
 		reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		return strings.ToLower(typ.Kind().String()), "", nil
 	case reflect.Array:
+		if typ.AssignableTo(abiUint256Type) { // uint256.Int underlying Go type is [4]uint64
+			return "uint256", "", nil
+		}
 		if typ.Elem().Kind() == reflect.Uint8 {
 			if typ.Len() == 20 && typ.Name() == "Address" {
 				return "address", "", nil
diff --git a/op-chain-ops/script/script.go b/op-chain-ops/script/script.go
index b6dfa5406230..e5630d0783c5 100644
--- a/op-chain-ops/script/script.go
+++ b/op-chain-ops/script/script.go
@@ -4,9 +4,12 @@ import (
 	"bytes"
 	"encoding/binary"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"math/big"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+
 	"github.com/holiman/uint256"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
@@ -19,13 +22,13 @@ import (
 	"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/ethdb"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/triedb"
 	"github.com/ethereum/go-ethereum/triedb/hashdb"
 
 	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
 	"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
 )
 
@@ -72,9 +75,12 @@ type Host struct {
 	af       *foundry.ArtifactsFS
 	chainCfg *params.ChainConfig
 	env      *vm.EVM
-	state    *state.StateDB
-	stateDB  state.Database
-	rawDB    ethdb.Database
+
+	state     *forking.ForkableState
+	baseState *state.StateDB
+
+	// only known contracts may utilize cheatcodes and logging
+	allowedCheatcodes map[common.Address]struct{}
 
 	cheatcodes *Precompile[*CheatCodesPrecompile]
 	console    *Precompile[*ConsolePrecompile]
@@ -117,6 +123,7 @@ type BroadcastHook func(broadcast Broadcast)
 
 type Hooks struct {
 	OnBroadcast BroadcastHook
+	OnFork      ForkHook
 }
 
 func WithBroadcastHook(hook BroadcastHook) HostOption {
@@ -125,6 +132,12 @@ func WithBroadcastHook(hook BroadcastHook) HostOption {
 	}
 }
 
+func WithForkHook(hook ForkHook) HostOption {
+	return func(h *Host) {
+		h.hooks.OnFork = hook
+	}
+}
+
 // 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.
@@ -167,7 +180,11 @@ func NewHost(
 		srcMaps:          make(map[common.Address]*srcmap.SourceMap),
 		hooks: &Hooks{
 			OnBroadcast: func(broadcast Broadcast) {},
+			OnFork: func(opts *ForkConfig) (forking.ForkSource, error) {
+				return nil, errors.New("no forking configured")
+			},
 		},
+		allowedCheatcodes: make(map[common.Address]struct{}),
 	}
 
 	for _, opt := range options {
@@ -212,18 +229,19 @@ func NewHost(
 	}
 
 	// Create an in-memory database, to host our temporary script state changes
-	h.rawDB = rawdb.NewMemoryDatabase()
-	h.stateDB = state.NewDatabase(triedb.NewDatabase(h.rawDB, &triedb.Config{
+	rawDB := rawdb.NewMemoryDatabase()
+	stateDB := state.NewDatabase(triedb.NewDatabase(rawDB, &triedb.Config{
 		Preimages: true, // To be able to iterate the state we need the Preimages
 		IsVerkle:  false,
 		HashDB:    hashdb.Defaults,
 		PathDB:    nil,
 	}), nil)
 	var err error
-	h.state, err = state.New(types.EmptyRootHash, h.stateDB)
+	h.baseState, err = state.New(types.EmptyRootHash, stateDB)
 	if err != nil {
 		panic(fmt.Errorf("failed to create memory state db: %w", err))
 	}
+	h.state = forking.NewForkableState(h.baseState)
 
 	// 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.
@@ -252,7 +270,7 @@ func NewHost(
 		GasPrice:     big.NewInt(0),
 		BlobHashes:   executionContext.BlobHashes,
 		BlobFeeCap:   big.NewInt(0),
-		AccessEvents: state.NewAccessEvents(h.stateDB.PointCache()),
+		AccessEvents: state.NewAccessEvents(h.baseState.PointCache()),
 	}
 
 	// Hook up the Host to capture the EVM environment changes
@@ -278,6 +296,18 @@ func NewHost(
 	return h
 }
 
+// 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
+}
+
 // 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})
@@ -288,8 +318,8 @@ func (h *Host) EnableCheats() error {
 	// 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
-	h.state.SetCode(VMAddr, []byte{0x00})
-	h.precompiles[VMAddr] = h.cheatcodes
+	h.state.SetCode(addresses.VMAddr, []byte{0x00})
+	h.precompiles[addresses.VMAddr] = h.cheatcodes
 
 	consolePrecompile, err := NewPrecompile[*ConsolePrecompile](&ConsolePrecompile{
 		logger: h.log,
@@ -299,7 +329,7 @@ func (h *Host) EnableCheats() error {
 		return fmt.Errorf("failed to init console precompile: %w", err)
 	}
 	h.console = consolePrecompile
-	h.precompiles[ConsoleAddr] = h.console
+	h.precompiles[addresses.ConsoleAddr] = h.console
 	// The Console precompile does not need bytecode,
 	// calls all go through a console lib, which avoids the EXTCODESIZE.
 	return nil
@@ -414,7 +444,7 @@ func (h *Host) ImportAccount(addr common.Address, account types.Account) {
 // 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 {
 	if p, ok := h.precompiles[addr]; ok {
-		return p
+		return &AccessControlledPrecompile{h: h, inner: p}
 	}
 	return original
 }
@@ -462,7 +492,7 @@ func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Addre
 	if !parentCallFrame.Prank.Broadcast {
 		return
 	}
-	if to == VMAddr || to == ConsoleAddr { // no broadcasts to the cheatcode or console address
+	if to == addresses.VMAddr || to == addresses.ConsoleAddr { // no broadcasts to the cheatcode or console address
 		return
 	}
 
@@ -561,6 +591,13 @@ func (h *Host) unwindCallstack(depth int) {
 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)
+	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())
+		}
+	}
 	// 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 {
@@ -609,11 +646,11 @@ func (h *Host) onLog(ev *types.Log) {
 
 // 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.
-func (h *Host) CurrentCall() CallFrame {
+func (h *Host) CurrentCall() *CallFrame {
 	if len(h.callStack) == 0 {
-		return CallFrame{}
+		return &CallFrame{}
 	}
-	return *h.callStack[len(h.callStack)-1]
+	return h.callStack[len(h.callStack)-1]
 }
 
 // MsgSender returns the msg.sender of the current active EVM call-frame,
@@ -652,27 +689,35 @@ func (h *Host) SetEnvVar(key string, value string) {
 // 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) {
+	if id, ok := h.state.ActiveFork(); ok {
+		return nil, fmt.Errorf("cannot state-dump while fork %s is active", id)
+	}
+	baseState := h.baseState
 	// We have to commit the existing state to the trie,
 	// for all the state-changes to be captured by the trie iterator.
-	root, err := h.state.Commit(h.env.Context.BlockNumber.Uint64(), true)
+	root, err := baseState.Commit(h.env.Context.BlockNumber.Uint64(), true)
 	if err != nil {
 		return nil, fmt.Errorf("failed to commit state: %w", err)
 	}
 	// We need a state object around the state DB
-	st, err := state.New(root, h.stateDB)
+	st, err := state.New(root, baseState.Database())
 	if err != nil {
 		return nil, fmt.Errorf("failed to create state object for state-dumping: %w", err)
 	}
 	// After Commit we cannot reuse the old State, so we update the host to use the new one
-	h.state = st
-	h.env.StateDB = st
+	h.baseState = st
+	h.state.SubstituteBaseState(st)
 
+	// 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).
 	var allocs foundry.ForgeAllocs
 	allocs.FromState(st)
 
 	// Sanity check we have no lingering scripts.
-	for i := uint64(0); i <= allocs.Accounts[ScriptDeployer].Nonce; i++ {
-		scriptAddr := crypto.CreateAddress(ScriptDeployer, i)
+	for i := uint64(0); i <= allocs.Accounts[addresses.ScriptDeployer].Nonce; i++ {
+		scriptAddr := crypto.CreateAddress(addresses.ScriptDeployer, i)
 		h.log.Info("removing script from state-dump", "addr", scriptAddr, "label", h.labels[scriptAddr])
 		delete(allocs.Accounts, scriptAddr)
 	}
@@ -694,12 +739,12 @@ func (h *Host) StateDump() (*foundry.ForgeAllocs, error) {
 	}
 
 	// Remove the script deployer from the output
-	delete(allocs.Accounts, ScriptDeployer)
-	delete(allocs.Accounts, ForgeDeployer)
+	delete(allocs.Accounts, addresses.ScriptDeployer)
+	delete(allocs.Accounts, addresses.ForgeDeployer)
 
 	// The cheatcodes VM has a placeholder bytecode,
 	// because solidity checks if the code exists prior to regular EVM-calls to it.
-	delete(allocs.Accounts, VMAddr)
+	delete(allocs.Accounts, addresses.VMAddr)
 
 	// Precompile overrides come with temporary state account placeholders. Ignore those.
 	for addr := range h.precompiles {
@@ -776,7 +821,7 @@ func (h *Host) Label(addr common.Address, label string) {
 
 // NewScriptAddress creates a new address for the ScriptDeployer account, and bumps the nonce.
 func (h *Host) NewScriptAddress() common.Address {
-	deployer := ScriptDeployer
+	deployer := addresses.ScriptDeployer
 	deployNonce := h.state.GetNonce(deployer)
 	// compute address of script contract to be deployed
 	addr := crypto.CreateAddress(deployer, deployNonce)
diff --git a/op-chain-ops/script/script_test.go b/op-chain-ops/script/script_test.go
index 9d029c801060..dbeef794fe37 100644
--- a/op-chain-ops/script/script_test.go
+++ b/op-chain-ops/script/script_test.go
@@ -2,12 +2,18 @@ package script
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"fmt"
 	"math/big"
 	"strings"
 	"testing"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
+	"github.com/stretchr/testify/mock"
+
 	"github.com/holiman/uint256"
 	"github.com/stretchr/testify/require"
 
@@ -23,16 +29,27 @@ import (
 
 //go:generate ./testdata/generate.sh
 
+// MockRPCClient implements RPCClient interface for testing
+type MockRPCClient struct {
+	mock.Mock
+}
+
+func (m *MockRPCClient) CallContext(ctx context.Context, result any, method string, args ...any) error {
+	return m.Called(ctx, result, method, args).Error(0)
+}
+
 func TestScript(t *testing.T) {
 	logger, captLog := testlog.CaptureLogger(t, log.LevelInfo)
 	af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
 
 	scriptContext := DefaultContext
 	h := NewHost(logger, af, nil, scriptContext)
+	require.NoError(t, h.EnableCheats())
+
 	addr, err := h.LoadContract("ScriptExample.s.sol", "ScriptExample")
 	require.NoError(t, err)
-
-	require.NoError(t, h.EnableCheats())
+	h.AllowCheatcodes(addr)
+	t.Logf("allowing %s to access cheatcodes", addr)
 
 	h.SetEnvVar("EXAMPLE_BOOL", "true")
 	input := bytes4("run()")
@@ -45,19 +62,19 @@ func TestScript(t *testing.T) {
 	require.NoError(t, h.cheatcodes.Precompile.DumpState("noop"))
 }
 
+func mustEncodeStringCalldata(t *testing.T, method, input string) []byte {
+	packer, err := abi.JSON(strings.NewReader(fmt.Sprintf(`[{"type":"function","name":"%s","inputs":[{"type":"string","name":"input"}]}]`, method)))
+	require.NoError(t, err)
+
+	data, err := packer.Pack(method, input)
+	require.NoError(t, err)
+	return data
+}
+
 func TestScriptBroadcast(t *testing.T) {
 	logger := testlog.Logger(t, log.LevelDebug)
 	af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
 
-	mustEncodeCalldata := func(method, input string) []byte {
-		packer, err := abi.JSON(strings.NewReader(fmt.Sprintf(`[{"type":"function","name":"%s","inputs":[{"type":"string","name":"input"}]}]`, method)))
-		require.NoError(t, err)
-
-		data, err := packer.Pack(method, input)
-		require.NoError(t, err)
-		return data
-	}
-
 	fooBar, err := af.ReadArtifact("ScriptExample.s.sol", "FooBar")
 	require.NoError(t, err)
 
@@ -74,7 +91,7 @@ func TestScriptBroadcast(t *testing.T) {
 		{
 			From:    scriptAddr,
 			To:      scriptAddr,
-			Input:   mustEncodeCalldata("call1", "single_call1"),
+			Input:   mustEncodeStringCalldata(t, "call1", "single_call1"),
 			Value:   (*hexutil.U256)(uint256.NewInt(0)),
 			GasUsed: 23421,
 			Type:    BroadcastCall,
@@ -83,7 +100,7 @@ func TestScriptBroadcast(t *testing.T) {
 		{
 			From:    coffeeAddr,
 			To:      scriptAddr,
-			Input:   mustEncodeCalldata("call1", "startstop_call1"),
+			Input:   mustEncodeStringCalldata(t, "call1", "startstop_call1"),
 			Value:   (*hexutil.U256)(uint256.NewInt(0)),
 			GasUsed: 1521,
 			Type:    BroadcastCall,
@@ -92,7 +109,7 @@ func TestScriptBroadcast(t *testing.T) {
 		{
 			From:    coffeeAddr,
 			To:      scriptAddr,
-			Input:   mustEncodeCalldata("call2", "startstop_call2"),
+			Input:   mustEncodeStringCalldata(t, "call2", "startstop_call2"),
 			Value:   (*hexutil.U256)(uint256.NewInt(0)),
 			GasUsed: 1565,
 			Type:    BroadcastCall,
@@ -101,7 +118,7 @@ func TestScriptBroadcast(t *testing.T) {
 		{
 			From:    common.HexToAddress("0x1234"),
 			To:      scriptAddr,
-			Input:   mustEncodeCalldata("nested1", "nested"),
+			Input:   mustEncodeStringCalldata(t, "nested1", "nested"),
 			Value:   (*hexutil.U256)(uint256.NewInt(0)),
 			GasUsed: 2763,
 			Type:    BroadcastCall,
@@ -142,10 +159,11 @@ func TestScriptBroadcast(t *testing.T) {
 		broadcasts = append(broadcasts, broadcast)
 	}
 	h := NewHost(logger, af, nil, DefaultContext, WithBroadcastHook(hook), WithCreate2Deployer())
+	require.NoError(t, h.EnableCheats())
+
 	addr, err := h.LoadContract("ScriptExample.s.sol", "ScriptExample")
 	require.NoError(t, err)
-
-	require.NoError(t, h.EnableCheats())
+	h.AllowCheatcodes(addr)
 
 	input := bytes4("runBroadcast()")
 	returnData, _, err := h.Call(senderAddr, addr, input[:], DefaultFoundryGasLimit, uint256.NewInt(0))
@@ -168,3 +186,163 @@ func TestScriptBroadcast(t *testing.T) {
 	// address that will perform the send to the Create2Deployer.
 	require.EqualValues(t, 1, h.GetNonce(cafeAddr))
 }
+
+func TestScriptStateDump(t *testing.T) {
+	logger := testlog.Logger(t, log.LevelDebug)
+	af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
+
+	h := NewHost(logger, af, nil, DefaultContext)
+	require.NoError(t, h.EnableCheats())
+
+	addr, err := h.LoadContract("ScriptExample.s.sol", "ScriptExample")
+	require.NoError(t, err)
+	h.AllowCheatcodes(addr)
+
+	counterStorageSlot := common.Hash{}
+
+	dump, err := h.StateDump()
+	require.NoError(t, err, "dump 1")
+	require.Contains(t, dump.Accounts, addr, "has contract")
+	require.NotContains(t, dump.Accounts[addr].Storage, counterStorageSlot, "not counted yet")
+
+	dat := mustEncodeStringCalldata(t, "call1", "call A")
+	returnData, _, err := h.Call(addresses.DefaultSenderAddr, addr, dat, DefaultFoundryGasLimit, uint256.NewInt(0))
+	require.NoError(t, err, "call A failed: %x", string(returnData))
+
+	dump, err = h.StateDump()
+	require.NoError(t, err, "dump 2")
+	require.Contains(t, dump.Accounts, addr, "has contract")
+	require.Equal(t, dump.Accounts[addr].Storage[counterStorageSlot], common.Hash{31: 1}, "counted to 1")
+
+	dat = mustEncodeStringCalldata(t, "call1", "call B")
+	returnData, _, err = h.Call(addresses.DefaultSenderAddr, addr, dat, DefaultFoundryGasLimit, uint256.NewInt(0))
+	require.NoError(t, err, "call B failed: %x", string(returnData))
+
+	dump, err = h.StateDump()
+	require.NoError(t, err, "dump 3")
+	require.Contains(t, dump.Accounts, addr, "has contract")
+	require.Equal(t, dump.Accounts[addr].Storage[counterStorageSlot], common.Hash{31: 2}, "counted to 2")
+}
+
+type forkConfig struct {
+	blockNum     uint64
+	stateRoot    common.Hash
+	blockHash    common.Hash
+	nonce        uint64
+	storageValue *big.Int
+	code         []byte
+	balance      uint64
+}
+
+func TestForkingScript(t *testing.T) {
+	logger := testlog.Logger(t, log.LevelInfo)
+	af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
+
+	forkedContract, err := af.ReadArtifact("ScriptExample.s.sol", "ForkedContract")
+	require.NoError(t, err)
+	code := forkedContract.DeployedBytecode.Object
+
+	fork1Config := forkConfig{
+		blockNum:     12345,
+		stateRoot:    common.HexToHash("0x1111"),
+		blockHash:    common.HexToHash("0x2222"),
+		nonce:        12345,
+		storageValue: big.NewInt(1),
+		code:         code,
+		balance:      1,
+	}
+
+	fork2Config := forkConfig{
+		blockNum:     23456,
+		stateRoot:    common.HexToHash("0x3333"),
+		blockHash:    common.HexToHash("0x4444"),
+		nonce:        23456,
+		storageValue: big.NewInt(2),
+		code:         code,
+		balance:      2,
+	}
+
+	// Map of URL/alias to RPC client
+	rpcClients := map[string]*MockRPCClient{
+		"fork1": setupMockRPC(fork1Config),
+		"fork2": setupMockRPC(fork2Config),
+	}
+	forkHook := func(opts *ForkConfig) (forking.ForkSource, error) {
+		client, ok := rpcClients[opts.URLOrAlias]
+		if !ok {
+			return nil, fmt.Errorf("unknown fork URL/alias: %s", opts.URLOrAlias)
+		}
+		return forking.RPCSourceByNumber(opts.URLOrAlias, client, *opts.BlockNumber)
+	}
+
+	scriptContext := DefaultContext
+	h := NewHost(logger, af, nil, scriptContext, WithForkHook(forkHook))
+	require.NoError(t, h.EnableCheats())
+
+	addr, err := h.LoadContract("ScriptExample.s.sol", "ForkTester")
+	require.NoError(t, err)
+	h.AllowCheatcodes(addr)
+	// Make this script persistent so it doesn't call the fork RPC.
+	h.state.MakePersistent(addr)
+	t.Logf("allowing %s to access cheatcodes", addr)
+
+	input := bytes4("run()")
+	returnData, _, err := h.Call(scriptContext.Sender, addr, input[:], DefaultFoundryGasLimit, uint256.NewInt(0))
+	require.NoError(t, err, "call failed: %x", string(returnData))
+
+	for _, client := range rpcClients {
+		client.AssertExpectations(t)
+	}
+}
+
+// setupMockRPC creates a mock RPC client with the specified fork configuration
+func setupMockRPC(config forkConfig) *MockRPCClient {
+	mockRPC := new(MockRPCClient)
+	testAddr := common.HexToAddress("0x1234")
+
+	forkArgs := []any{testAddr, config.blockHash}
+
+	// Mock block header
+	mockRPC.On("CallContext", mock.Anything, mock.AnythingOfType("**forking.Header"),
+		"eth_getBlockByNumber", []any{hexutil.Uint64(config.blockNum), false}).
+		Run(func(args mock.Arguments) {
+			result := args.Get(1).(**forking.Header)
+			*result = &forking.Header{
+				StateRoot: config.stateRoot,
+				BlockHash: config.blockHash,
+			}
+		}).Return(nil).Once()
+
+	mockRPC.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Uint64"),
+		"eth_getTransactionCount", forkArgs).
+		Run(func(args mock.Arguments) {
+			result := args.Get(1).(*hexutil.Uint64)
+			*result = hexutil.Uint64(config.nonce)
+		}).Return(nil)
+
+	// Mock balance
+	mockRPC.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.U256"),
+		"eth_getBalance", forkArgs).
+		Run(func(args mock.Arguments) {
+			result := args.Get(1).(*hexutil.U256)
+			*result = hexutil.U256(*uint256.NewInt(config.balance))
+		}).Return(nil)
+
+	// Mock contract code
+	mockRPC.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"),
+		"eth_getCode", forkArgs).
+		Run(func(args mock.Arguments) {
+			result := args.Get(1).(*hexutil.Bytes)
+			*result = config.code
+		}).Return(nil)
+
+	// Mock storage value
+	mockRPC.On("CallContext", mock.Anything, mock.AnythingOfType("*common.Hash"),
+		"eth_getStorageAt", []any{testAddr, common.Hash{}, config.blockHash}).
+		Run(func(args mock.Arguments) {
+			result := args.Get(1).(*common.Hash)
+			*result = common.BigToHash(config.storageValue)
+		}).Return(nil)
+
+	return mockRPC
+}
diff --git a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol
index f9ea6239d433..2f636afb2be3 100644
--- a/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol
+++ b/op-chain-ops/script/testdata/scripts/ScriptExample.s.sol
@@ -13,6 +13,10 @@ interface Vm {
     function startBroadcast(address msgSender) external;
     function startBroadcast() external;
     function stopBroadcast() external;
+    function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode);
+    function etch(address target, bytes calldata newRuntimeBytecode) external;
+    function allowCheatcodes(address account) external;
+    function createSelectFork(string calldata forkName, uint256 blockNumber) external returns (uint256);
 }
 
 // console is a minimal version of the console2 lib.
@@ -96,6 +100,13 @@ contract ScriptExample {
         vm.stopPrank();
         this.hello("from original again");
 
+        // vm.etch should not give cheatcode access, unless allowed to afterwards
+        address tmpNonceGetter = address(uint160(uint256(keccak256("temp nonce test getter"))));
+        vm.etch(tmpNonceGetter, vm.getDeployedCode("ScriptExample.s.sol:NonceGetter"));
+        vm.allowCheatcodes(tmpNonceGetter);
+        uint256 v = NonceGetter(tmpNonceGetter).getNonce(address(this));
+        console.log("nonce from nonce getter, no explicit access required with vm.etch:", v);
+
         console.log("done!");
     }
 
@@ -177,3 +188,44 @@ contract FooBar {
         foo = v;
     }
 }
+
+contract NonceGetter {
+    address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
+    Vm internal constant vm = Vm(VM_ADDRESS);
+
+    function getNonce(address _addr) public view returns (uint256) {
+        return vm.getNonce(_addr);
+    }
+}
+
+contract ForkedContract {
+    uint256 internal v;
+
+    constructor() {
+        v = 1;
+    }
+
+    function getValue() public view returns (uint256) {
+        return v;
+    }
+}
+
+contract ForkTester {
+    address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
+    Vm internal constant vm = Vm(VM_ADDRESS);
+
+    function run() external {
+        address testAddr = address(uint160(0x1234));
+        ForkedContract fc = ForkedContract(testAddr);
+
+        vm.createSelectFork("fork1", 12345);
+        require(vm.getNonce(testAddr) == 12345, "nonce should be 12345");
+        require(fc.getValue() == 1, "value should be 1");
+        require(testAddr.balance == uint256(1), "balance should be 1");
+
+        vm.createSelectFork("fork2", 23456);
+        require(vm.getNonce(testAddr) == 23456, "nonce should be 12345");
+        require(fc.getValue() == 2, "value should be 2");
+        require(testAddr.balance == uint256(2), "balance should be 2");
+    }
+}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/FooBar.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/FooBar.json
index ad4ac1569433..249588154e11 100644
--- a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/FooBar.json
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/FooBar.json
@@ -1 +1 @@
-{"abi":[{"type":"constructor","inputs":[{"name":"v","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"foo","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"5902:96:0:-:0;;;5949:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5982:3;:7;5902:96;;14:184:1;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;-1:-1:-1;176:16:1;;14:184;-1:-1:-1;14:184:1:o;:::-;5902:96:0;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"5902:96:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5924:18;;;;;;;;;160:25:1;;;148:2;133:18;5924::0;;;;;;","linkReferences":{}},"methodIdentifiers":{"foo()":"c2985578"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"foo\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"FooBar\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3\",\"dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"v","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"stateMutability":"view","type":"function","name":"foo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"FooBar"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d","urls":["bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3","dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[{"astId":708,"contract":"scripts/ScriptExample.s.sol:FooBar","label":"foo","offset":0,"slot":"0","type":"t_uint256"}],"types":{"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":720,"exportedSymbols":{"FooBar":[719],"ScriptExample":[706],"Vm":[55],"console":[192]},"nodeType":"SourceUnit","src":"32:5967:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":55,"nodeType":"ContractDefinition","src":"120:616:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":55,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[55],"name":"Vm","nameLocation":"130:2:0","scope":720,"usedErrors":[]},{"id":192,"nodeType":"ContractDefinition","src":"791:1622:0","nodes":[{"id":61,"nodeType":"VariableDeclaration","src":"813:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"830:15:0","scope":192,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":56,"name":"address","nodeType":"ElementaryTypeName","src":"813:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":59,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"856:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":58,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"848:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":57,"name":"address","nodeType":"ElementaryTypeName","src":"848:7:0","typeDescriptions":{}}},"id":60,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"848:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":78,"nodeType":"FunctionDefinition","src":"906:221:0","nodes":[],"body":{"id":77,"nodeType":"Block","src":"1065:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1084:37:0","statements":[{"nodeType":"YulAssignment","src":"1098:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1107:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1098:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":67,"isOffset":false,"isSlot":false,"src":"1107:4:0","valueSize":1},{"declaration":74,"isOffset":false,"isSlot":false,"src":"1098:5:0","valueSize":1}],"id":76,"nodeType":"InlineAssembly","src":"1075:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"915:25:0","parameters":{"id":68,"nodeType":"ParameterList","parameters":[{"constant":false,"id":67,"mutability":"mutable","name":"fnIn","nameLocation":"987:4:0","nodeType":"VariableDeclaration","scope":78,"src":"950:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":66,"nodeType":"FunctionTypeName","parameterTypes":{"id":64,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":66,"src":"959:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":62,"name":"bytes","nodeType":"ElementaryTypeName","src":"959:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"958:14:0"},"returnParameterTypes":{"id":65,"nodeType":"ParameterList","parameters":[],"src":"987:0:0"},"src":"950:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"940:57:0"},"returnParameters":{"id":75,"nodeType":"ParameterList","parameters":[{"constant":false,"id":74,"mutability":"mutable","name":"fnOut","nameLocation":"1058:5:0","nodeType":"VariableDeclaration","scope":78,"src":"1021:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":73,"nodeType":"FunctionTypeName","parameterTypes":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":73,"src":"1030:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":69,"name":"bytes","nodeType":"ElementaryTypeName","src":"1030:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1029:14:0"},"returnParameterTypes":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"1058:0:0"},"src":"1021:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1020:44:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":90,"nodeType":"FunctionDefinition","src":"1133:133:0","nodes":[],"body":{"id":89,"nodeType":"Block","src":"1194:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":86,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":80,"src":"1251:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":84,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1230:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":83,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":78,"src":"1204:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":85,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":87,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":88,"nodeType":"ExpressionStatement","src":"1204:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1142:15:0","parameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"payload","nameLocation":"1171:7:0","nodeType":"VariableDeclaration","scope":90,"src":"1158:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":79,"name":"bytes","nodeType":"ElementaryTypeName","src":"1158:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1157:22:0"},"returnParameters":{"id":82,"nodeType":"ParameterList","parameters":[],"src":"1194:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1272:380:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1336:316:0","nodes":[],"statements":[{"assignments":[96],"declarations":[{"constant":false,"id":96,"mutability":"mutable","name":"payloadLength","nameLocation":"1354:13:0","nodeType":"VariableDeclaration","scope":105,"src":"1346:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":95,"name":"uint256","nodeType":"ElementaryTypeName","src":"1346:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":99,"initialValue":{"expression":{"id":97,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":92,"src":"1370:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":98,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1370:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1346:38:0"},{"assignments":[101],"declarations":[{"constant":false,"id":101,"mutability":"mutable","name":"consoleAddress","nameLocation":"1402:14:0","nodeType":"VariableDeclaration","scope":105,"src":"1394:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":100,"name":"address","nodeType":"ElementaryTypeName","src":"1394:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":103,"initialValue":{"id":102,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":61,"src":"1419:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1394:40:0"},{"AST":{"nodeType":"YulBlock","src":"1496:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1510:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1534:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1543:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1530:3:0"},"nodeType":"YulFunctionCall","src":"1530:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1514:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1559:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1579:3:0"},"nodeType":"YulFunctionCall","src":"1579:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1586:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1602:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1616:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1631:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1634:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1568:10:0"},"nodeType":"YulFunctionCall","src":"1568:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1563:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":101,"isOffset":false,"isSlot":false,"src":"1586:14:0","valueSize":1},{"declaration":92,"isOffset":false,"isSlot":false,"src":"1534:7:0","valueSize":1},{"declaration":96,"isOffset":false,"isSlot":false,"src":"1616:13:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1487:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1281:19:0","parameters":{"id":93,"nodeType":"ParameterList","parameters":[{"constant":false,"id":92,"mutability":"mutable","name":"payload","nameLocation":"1314:7:0","nodeType":"VariableDeclaration","scope":106,"src":"1301:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":91,"name":"bytes","nodeType":"ElementaryTypeName","src":"1301:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1300:22:0"},"returnParameters":{"id":94,"nodeType":"ParameterList","parameters":[],"src":"1336:0:0"},"scope":192,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":120,"nodeType":"FunctionDefinition","src":"1658:121:0","nodes":[],"body":{"id":119,"nodeType":"Block","src":"1703:76:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":114,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1753:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":115,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1768:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":112,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1729:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":113,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1729:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":116,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1729:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":111,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1713:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":117,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1713:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":118,"nodeType":"ExpressionStatement","src":"1713:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1667:3:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"p0","nameLocation":"1685:2:0","nodeType":"VariableDeclaration","scope":120,"src":"1671:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":107,"name":"string","nodeType":"ElementaryTypeName","src":"1671:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1670:18:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":137,"nodeType":"FunctionDefinition","src":"1785:139:0","nodes":[],"body":{"id":136,"nodeType":"Block","src":"1839:85:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":130,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1889:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":131,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":122,"src":"1909:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":132,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":124,"src":"1913:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":128,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1865:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":129,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1865:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":133,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1865:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":127,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1849:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":134,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1849:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":135,"nodeType":"ExpressionStatement","src":"1849:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1794:3:0","parameters":{"id":125,"nodeType":"ParameterList","parameters":[{"constant":false,"id":122,"mutability":"mutable","name":"p0","nameLocation":"1812:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1798:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":121,"name":"string","nodeType":"ElementaryTypeName","src":"1798:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":124,"mutability":"mutable","name":"p1","nameLocation":"1821:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1816:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":123,"name":"bool","nodeType":"ElementaryTypeName","src":"1816:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"1797:27:0"},"returnParameters":{"id":126,"nodeType":"ParameterList","parameters":[],"src":"1839:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":154,"nodeType":"FunctionDefinition","src":"1930:145:0","nodes":[],"body":{"id":153,"nodeType":"Block","src":"1987:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":147,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2037:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":148,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":139,"src":"2060:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":149,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":141,"src":"2064:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":145,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2013:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":146,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2013:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":150,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2013:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":144,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1997:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":151,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1997:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":152,"nodeType":"ExpressionStatement","src":"1997:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1939:3:0","parameters":{"id":142,"nodeType":"ParameterList","parameters":[{"constant":false,"id":139,"mutability":"mutable","name":"p0","nameLocation":"1957:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1943:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":138,"name":"string","nodeType":"ElementaryTypeName","src":"1943:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":141,"mutability":"mutable","name":"p1","nameLocation":"1969:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1961:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":140,"name":"uint256","nodeType":"ElementaryTypeName","src":"1961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1942:30:0"},"returnParameters":{"id":143,"nodeType":"ParameterList","parameters":[],"src":"1987:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":171,"nodeType":"FunctionDefinition","src":"2081:145:0","nodes":[],"body":{"id":170,"nodeType":"Block","src":"2138:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":164,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2188:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":165,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":156,"src":"2211:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":166,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":158,"src":"2215:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":162,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2164:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":163,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2164:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":167,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2164:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":161,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2148:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":168,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2148:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":169,"nodeType":"ExpressionStatement","src":"2148:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2090:3:0","parameters":{"id":159,"nodeType":"ParameterList","parameters":[{"constant":false,"id":156,"mutability":"mutable","name":"p0","nameLocation":"2108:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2094:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":155,"name":"string","nodeType":"ElementaryTypeName","src":"2094:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":158,"mutability":"mutable","name":"p1","nameLocation":"2120:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2112:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":157,"name":"address","nodeType":"ElementaryTypeName","src":"2112:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2093:30:0"},"returnParameters":{"id":160,"nodeType":"ParameterList","parameters":[],"src":"2138:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":191,"nodeType":"FunctionDefinition","src":"2232:179:0","nodes":[],"body":{"id":190,"nodeType":"Block","src":"2313:98:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":183,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2363:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":184,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":173,"src":"2392:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":185,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":175,"src":"2396:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":186,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":177,"src":"2400:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":181,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2339:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":182,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2339:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":187,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2339:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":180,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2323:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":188,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2323:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":189,"nodeType":"ExpressionStatement","src":"2323:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2241:3:0","parameters":{"id":178,"nodeType":"ParameterList","parameters":[{"constant":false,"id":173,"mutability":"mutable","name":"p0","nameLocation":"2259:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2245:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":172,"name":"string","nodeType":"ElementaryTypeName","src":"2245:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":175,"mutability":"mutable","name":"p1","nameLocation":"2277:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2263:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":174,"name":"string","nodeType":"ElementaryTypeName","src":"2263:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":177,"mutability":"mutable","name":"p2","nameLocation":"2295:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2281:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":176,"name":"string","nodeType":"ElementaryTypeName","src":"2281:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2244:54:0"},"returnParameters":{"id":179,"nodeType":"ParameterList","parameters":[],"src":"2313:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[192],"name":"console","nameLocation":"799:7:0","scope":720,"usedErrors":[]},{"id":706,"nodeType":"ContractDefinition","src":"2541:3359:0","nodes":[{"id":207,"nodeType":"VariableDeclaration","src":"2571:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"2597:10:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":194,"name":"address","nodeType":"ElementaryTypeName","src":"2571:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":202,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2644:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":201,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"2634:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":203,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2634:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":200,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2626:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":199,"name":"uint256","nodeType":"ElementaryTypeName","src":"2626:7:0","typeDescriptions":{}}},"id":204,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2626:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":198,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2618:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":197,"name":"uint160","nodeType":"ElementaryTypeName","src":"2618:7:0","typeDescriptions":{}}},"id":205,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2618:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":196,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2610:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":195,"name":"address","nodeType":"ElementaryTypeName","src":"2610:7:0","typeDescriptions":{}}},"id":206,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2610:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":213,"nodeType":"VariableDeclaration","src":"2671:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"2692:2:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"},"typeName":{"id":209,"nodeType":"UserDefinedTypeName","pathNode":{"id":208,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":55,"src":"2671:2:0"},"referencedDeclaration":55,"src":"2671:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"value":{"arguments":[{"id":211,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":207,"src":"2700:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":210,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":55,"src":"2697:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$55_$","typeString":"type(contract Vm)"}},"id":212,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2697:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"visibility":"internal"},{"id":215,"nodeType":"VariableDeclaration","src":"2775:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"2790:7:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":214,"name":"uint256","nodeType":"ElementaryTypeName","src":"2775:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":378,"nodeType":"FunctionDefinition","src":"2887:949:0","nodes":[],"body":{"id":377,"nodeType":"Block","src":"2909:927:0","nodes":[],"statements":[{"assignments":[220],"declarations":[{"constant":false,"id":220,"mutability":"mutable","name":"x","nameLocation":"2924:1:0","nodeType":"VariableDeclaration","scope":377,"src":"2919:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":219,"name":"bool","nodeType":"ElementaryTypeName","src":"2919:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":226,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":223,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2937:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":224,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"2953:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":221,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"2928:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":222,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"2928:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":225,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2928:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"2919:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2981:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":231,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3004:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":227,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"2969:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":229,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":137,"src":"2969:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":232,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2969:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":233,"nodeType":"ExpressionStatement","src":"2969:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":237,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3029:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":240,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3054:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":239,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3046:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":238,"name":"address","nodeType":"ElementaryTypeName","src":"3046:7:0","typeDescriptions":{}}},"id":241,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3046:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":234,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3017:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":236,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3017:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":242,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3017:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":243,"nodeType":"ExpressionStatement","src":"3017:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":247,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3082:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":252,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3120:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":251,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3112:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":250,"name":"address","nodeType":"ElementaryTypeName","src":"3112:7:0","typeDescriptions":{}}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3112:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":248,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3100:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":249,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3100:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":254,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3100:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":244,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3070:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":246,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3070:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":255,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3070:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":256,"nodeType":"ExpressionStatement","src":"3070:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":260,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3149:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":263,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3172:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3172:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":262,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3164:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":261,"name":"address","nodeType":"ElementaryTypeName","src":"3164:7:0","typeDescriptions":{}}},"id":265,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3164:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":257,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3137:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":259,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3137:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":266,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3137:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":267,"nodeType":"ExpressionStatement","src":"3137:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":271,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3206:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":276,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3242:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3242:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":275,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3234:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":274,"name":"address","nodeType":"ElementaryTypeName","src":"3234:7:0","typeDescriptions":{}}},"id":278,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3234:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":272,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3222:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":273,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3222:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":279,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3222:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":268,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3194:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3194:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":280,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3194:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":281,"nodeType":"ExpressionStatement","src":"3194:61:0"},{"assignments":[283],"declarations":[{"constant":false,"id":283,"mutability":"mutable","name":"json","nameLocation":"3280:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3266:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":282,"name":"string","nodeType":"ElementaryTypeName","src":"3266:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":285,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":284,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3287:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3266:55:0"},{"assignments":[290],"declarations":[{"constant":false,"id":290,"mutability":"mutable","name":"keys","nameLocation":"3347:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3331:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":288,"name":"string","nodeType":"ElementaryTypeName","src":"3331:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":289,"nodeType":"ArrayTypeName","src":"3331:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":296,"initialValue":{"arguments":[{"id":293,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":283,"src":"3371:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":294,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3377:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":291,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3354:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3354:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":295,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3354:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3331:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":300,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3414:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":301,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3422:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":303,"indexExpression":{"hexValue":"30","id":302,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3427:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3422:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":304,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3431:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":306,"indexExpression":{"hexValue":"31","id":305,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3436:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3431:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":297,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3402:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":299,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":191,"src":"3402:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3402:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":308,"nodeType":"ExpressionStatement","src":"3402:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3461:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":309,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3450:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":311,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3450:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":313,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3450:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":314,"nodeType":"ExpressionStatement","src":"3450:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3517:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":321,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3509:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":320,"name":"uint160","nodeType":"ElementaryTypeName","src":"3509:7:0","typeDescriptions":{}}},"id":323,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":319,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3501:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":318,"name":"address","nodeType":"ElementaryTypeName","src":"3501:7:0","typeDescriptions":{}}},"id":324,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3501:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":315,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3487:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":317,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"3487:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":325,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3487:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":326,"nodeType":"ExpressionStatement","src":"3487:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3545:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":327,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3534:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":329,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3534:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":331,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3534:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":332,"nodeType":"ExpressionStatement","src":"3534:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":336,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3582:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":339,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3617:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":340,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3617:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":338,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3609:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":337,"name":"address","nodeType":"ElementaryTypeName","src":"3609:7:0","typeDescriptions":{}}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3609:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":333,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3570:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3570:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":342,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3570:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":343,"nodeType":"ExpressionStatement","src":"3570:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":347,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3651:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":350,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3689:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3681:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":348,"name":"address","nodeType":"ElementaryTypeName","src":"3681:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":344,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3639:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":346,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3639:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":352,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3639:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":353,"nodeType":"ExpressionStatement","src":"3639:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":357,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3716:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":354,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3705:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":356,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3705:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":358,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3705:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":359,"nodeType":"ExpressionStatement","src":"3705:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":360,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3741:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":362,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"3741:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3741:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":364,"nodeType":"ExpressionStatement","src":"3741:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":368,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3776:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":365,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3765:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":367,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3765:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3765:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":370,"nodeType":"ExpressionStatement","src":"3765:33:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":374,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3821:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":371,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3809:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":373,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"3809:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":375,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3809:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":376,"nodeType":"ExpressionStatement","src":"3809:20:0"}]},"documentation":{"id":216,"nodeType":"StructuredDocumentation","src":"2804:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"2896:3:0","parameters":{"id":217,"nodeType":"ParameterList","parameters":[],"src":"2899:2:0"},"returnParameters":{"id":218,"nodeType":"ParameterList","parameters":[],"src":"2909:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":609,"nodeType":"FunctionDefinition","src":"3903:1258:0","nodes":[],"body":{"id":608,"nodeType":"Block","src":"3934:1227:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3956:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":392,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3999:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":391,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3991:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":390,"name":"address","nodeType":"ElementaryTypeName","src":"3991:7:0","typeDescriptions":{}}},"id":393,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3991:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3979:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":389,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3979:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":394,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3979:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":387,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3971:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":386,"name":"uint256","nodeType":"ElementaryTypeName","src":"3971:7:0","typeDescriptions":{}}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3971:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":382,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3944:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3944:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":396,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3944:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":397,"nodeType":"ExpressionStatement","src":"3944:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":401,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4030:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":398,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4018:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":400,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4018:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":402,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4018:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":403,"nodeType":"ExpressionStatement","src":"4018:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":404,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4057:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":406,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"4057:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":407,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4057:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":408,"nodeType":"ExpressionStatement","src":"4057:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4092:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":409,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4081:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":411,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4081:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":413,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4081:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":414,"nodeType":"ExpressionStatement","src":"4081:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":418,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4128:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":415,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4117:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":417,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4117:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4117:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":420,"nodeType":"ExpressionStatement","src":"4117:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":424,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4166:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":421,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4154:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":423,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4154:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":425,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4154:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":426,"nodeType":"ExpressionStatement","src":"4154:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":434,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4231:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":433,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4223:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":432,"name":"uint160","nodeType":"ElementaryTypeName","src":"4223:7:0","typeDescriptions":{}}},"id":435,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4223:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":431,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4215:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":430,"name":"address","nodeType":"ElementaryTypeName","src":"4215:7:0","typeDescriptions":{}}},"id":436,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4215:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":427,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4197:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":429,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4197:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":437,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4197:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":438,"nodeType":"ExpressionStatement","src":"4197:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":442,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4263:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":439,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4252:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":441,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4252:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":443,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4252:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":444,"nodeType":"ExpressionStatement","src":"4252:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":448,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4302:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":445,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4291:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4291:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":449,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4291:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":450,"nodeType":"ExpressionStatement","src":"4291:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":454,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4344:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":451,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4330:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":705,"src":"4330:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":455,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4330:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":456,"nodeType":"ExpressionStatement","src":"4330:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":457,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4371:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":459,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4371:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":460,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4371:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":461,"nodeType":"ExpressionStatement","src":"4371:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":465,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4410:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":462,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4399:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":464,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4399:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":466,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4399:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":467,"nodeType":"ExpressionStatement","src":"4399:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":471,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4451:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":468,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4439:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":470,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4439:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4439:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":473,"nodeType":"ExpressionStatement","src":"4439:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":481,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4512:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":480,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4504:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":479,"name":"uint160","nodeType":"ElementaryTypeName","src":"4504:7:0","typeDescriptions":{}}},"id":482,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4504:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":478,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4496:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":477,"name":"address","nodeType":"ElementaryTypeName","src":"4496:7:0","typeDescriptions":{}}},"id":483,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4496:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":474,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4478:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":476,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4478:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4478:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":485,"nodeType":"ExpressionStatement","src":"4478:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":489,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4544:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":486,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4531:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":488,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":678,"src":"4531:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":490,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4531:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":491,"nodeType":"ExpressionStatement","src":"4531:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":492,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4563:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":494,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4563:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4563:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":496,"nodeType":"ExpressionStatement","src":"4563:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":500,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4604:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":497,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4592:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":499,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4592:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4592:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":502,"nodeType":"ExpressionStatement","src":"4592:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":510,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4665:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4657:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":508,"name":"uint160","nodeType":"ElementaryTypeName","src":"4657:7:0","typeDescriptions":{}}},"id":511,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":507,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4649:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":506,"name":"address","nodeType":"ElementaryTypeName","src":"4649:7:0","typeDescriptions":{}}},"id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4649:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":503,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4636:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":505,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4636:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":513,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4636:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":514,"nodeType":"ExpressionStatement","src":"4636:40:0"},{"assignments":[517],"declarations":[{"constant":false,"id":517,"mutability":"mutable","name":"x","nameLocation":"4693:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4686:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":516,"nodeType":"UserDefinedTypeName","pathNode":{"id":515,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4686:6:0"},"referencedDeclaration":719,"src":"4686:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":523,"initialValue":{"arguments":[{"hexValue":"31323334","id":521,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4708:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":520,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4697:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":519,"nodeType":"UserDefinedTypeName","pathNode":{"id":518,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4701:6:0"},"referencedDeclaration":719,"src":"4701:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":522,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4697:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4686:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":529,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":525,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":517,"src":"4731:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":526,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4731:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4731:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":528,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4742:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4731:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":524,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4723:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":530,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4723:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":531,"nodeType":"ExpressionStatement","src":"4723:24:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":535,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4770:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":532,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4758:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":534,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4758:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":536,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4758:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":537,"nodeType":"ExpressionStatement","src":"4758:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":545,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4820:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":544,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4812:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":543,"name":"uint160","nodeType":"ElementaryTypeName","src":"4812:7:0","typeDescriptions":{}}},"id":546,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4812:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":542,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4804:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":541,"name":"address","nodeType":"ElementaryTypeName","src":"4804:7:0","typeDescriptions":{}}},"id":547,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4804:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":538,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4791:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":540,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4791:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4791:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":549,"nodeType":"ExpressionStatement","src":"4791:38:0"},{"assignments":[552],"declarations":[{"constant":false,"id":552,"mutability":"mutable","name":"y","nameLocation":"4846:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4839:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":551,"nodeType":"UserDefinedTypeName","pathNode":{"id":550,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4839:6:0"},"referencedDeclaration":719,"src":"4839:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":566,"initialValue":{"arguments":[{"hexValue":"31323334","id":564,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4889:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":555,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4850:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":554,"nodeType":"UserDefinedTypeName","pathNode":{"id":553,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4854:6:0"},"referencedDeclaration":719,"src":"4854:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":563,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4883:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":559,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4875:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":558,"name":"uint256","nodeType":"ElementaryTypeName","src":"4875:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4875:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":557,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4867:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":556,"name":"bytes32","nodeType":"ElementaryTypeName","src":"4867:7:0","typeDescriptions":{}}},"id":562,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4867:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"4850:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":565,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4850:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4839:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":568,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":552,"src":"4912:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":569,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4912:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":570,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4912:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":571,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4923:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4912:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":567,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4904:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4904:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"4904:24:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4950:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4938:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4938:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4938:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"4938:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5042:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5042:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":584,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5042:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":585,"nodeType":"ExpressionStatement","src":"5042:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5077:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":588,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5066:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":587,"nodeType":"UserDefinedTypeName","pathNode":{"id":586,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"5070:6:0"},"referencedDeclaration":719,"src":"5070:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":590,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5066:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":591,"nodeType":"ExpressionStatement","src":"5066:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":595,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5105:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":602,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5146:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":601,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5138:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":600,"name":"address","nodeType":"ElementaryTypeName","src":"5138:7:0","typeDescriptions":{}}},"id":603,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5138:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":598,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5126:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":599,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5126:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5126:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":597,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5118:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":596,"name":"uint256","nodeType":"ElementaryTypeName","src":"5118:7:0","typeDescriptions":{}}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5118:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":592,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5093:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":594,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"5093:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":606,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5093:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":607,"nodeType":"ExpressionStatement","src":"5093:61:0"}]},"documentation":{"id":379,"nodeType":"StructuredDocumentation","src":"3842:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"3912:12:0","parameters":{"id":380,"nodeType":"ParameterList","parameters":[],"src":"3924:2:0"},"returnParameters":{"id":381,"nodeType":"ParameterList","parameters":[],"src":"3934:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":633,"nodeType":"FunctionDefinition","src":"5256:143:0","nodes":[],"body":{"id":632,"nodeType":"Block","src":"5305:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":618,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":612,"src":"5327:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":615,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5315:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":617,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5315:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5315:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":620,"nodeType":"ExpressionStatement","src":"5315:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5352:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":627,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"5380:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":628,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"5380:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":626,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5372:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":625,"name":"address","nodeType":"ElementaryTypeName","src":"5372:7:0","typeDescriptions":{}}},"id":629,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5372:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":621,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5340:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":623,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"5340:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":630,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5340:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":631,"nodeType":"ExpressionStatement","src":"5340:52:0"}]},"documentation":{"id":610,"nodeType":"StructuredDocumentation","src":"5167:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"5265:5:0","parameters":{"id":613,"nodeType":"ParameterList","parameters":[{"constant":false,"id":612,"mutability":"mutable","name":"_v","nameLocation":"5287:2:0","nodeType":"VariableDeclaration","scope":633,"src":"5271:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":611,"name":"string","nodeType":"ElementaryTypeName","src":"5271:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5270:20:0"},"returnParameters":{"id":614,"nodeType":"ParameterList","parameters":[],"src":"5305:0:0"},"scope":706,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":648,"nodeType":"FunctionDefinition","src":"5405:95:0","nodes":[],"body":{"id":647,"nodeType":"Block","src":"5449:51:0","nodes":[],"statements":[{"expression":{"id":639,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5459:9:0","subExpression":{"id":638,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5459:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":640,"nodeType":"ExpressionStatement","src":"5459:9:0"},{"expression":{"arguments":[{"id":644,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":635,"src":"5490:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":641,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5478:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":643,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5478:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":645,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5478:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":646,"nodeType":"ExpressionStatement","src":"5478:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"5414:5:0","parameters":{"id":636,"nodeType":"ParameterList","parameters":[{"constant":false,"id":635,"mutability":"mutable","name":"_v","nameLocation":"5436:2:0","nodeType":"VariableDeclaration","scope":648,"src":"5420:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":634,"name":"string","nodeType":"ElementaryTypeName","src":"5420:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5419:20:0"},"returnParameters":{"id":637,"nodeType":"ParameterList","parameters":[],"src":"5449:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":663,"nodeType":"FunctionDefinition","src":"5506:95:0","nodes":[],"body":{"id":662,"nodeType":"Block","src":"5550:51:0","nodes":[],"statements":[{"expression":{"id":654,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5560:9:0","subExpression":{"id":653,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5560:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":655,"nodeType":"ExpressionStatement","src":"5560:9:0"},{"expression":{"arguments":[{"id":659,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":650,"src":"5591:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":656,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5579:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":658,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5579:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":660,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5579:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":661,"nodeType":"ExpressionStatement","src":"5579:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"5515:5:0","parameters":{"id":651,"nodeType":"ParameterList","parameters":[{"constant":false,"id":650,"mutability":"mutable","name":"_v","nameLocation":"5537:2:0","nodeType":"VariableDeclaration","scope":663,"src":"5521:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":649,"name":"string","nodeType":"ElementaryTypeName","src":"5521:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5520:20:0"},"returnParameters":{"id":652,"nodeType":"ParameterList","parameters":[],"src":"5550:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":678,"nodeType":"FunctionDefinition","src":"5607:98:0","nodes":[],"body":{"id":677,"nodeType":"Block","src":"5653:52:0","nodes":[],"statements":[{"expression":{"id":669,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5663:9:0","subExpression":{"id":668,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5663:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":670,"nodeType":"ExpressionStatement","src":"5663:9:0"},{"expression":{"arguments":[{"id":674,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":665,"src":"5695:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":671,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5682:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":673,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":693,"src":"5682:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":675,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5682:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":676,"nodeType":"ExpressionStatement","src":"5682:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"5616:7:0","parameters":{"id":666,"nodeType":"ParameterList","parameters":[{"constant":false,"id":665,"mutability":"mutable","name":"_v","nameLocation":"5640:2:0","nodeType":"VariableDeclaration","scope":678,"src":"5624:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":664,"name":"string","nodeType":"ElementaryTypeName","src":"5624:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5623:20:0"},"returnParameters":{"id":667,"nodeType":"ParameterList","parameters":[],"src":"5653:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":693,"nodeType":"FunctionDefinition","src":"5711:97:0","nodes":[],"body":{"id":692,"nodeType":"Block","src":"5757:51:0","nodes":[],"statements":[{"expression":{"id":684,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5767:9:0","subExpression":{"id":683,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5767:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":685,"nodeType":"ExpressionStatement","src":"5767:9:0"},{"expression":{"arguments":[{"id":689,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":680,"src":"5798:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":686,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5786:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":688,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5786:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":690,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5786:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":691,"nodeType":"ExpressionStatement","src":"5786:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"5720:7:0","parameters":{"id":681,"nodeType":"ParameterList","parameters":[{"constant":false,"id":680,"mutability":"mutable","name":"_v","nameLocation":"5744:2:0","nodeType":"VariableDeclaration","scope":693,"src":"5728:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":679,"name":"string","nodeType":"ElementaryTypeName","src":"5728:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5727:20:0"},"returnParameters":{"id":682,"nodeType":"ParameterList","parameters":[],"src":"5757:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":705,"nodeType":"FunctionDefinition","src":"5814:84:0","nodes":[],"body":{"id":704,"nodeType":"Block","src":"5866:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":701,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":695,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":698,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5876:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":700,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5876:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":702,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5876:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":703,"nodeType":"ExpressionStatement","src":"5876:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"5823:8:0","parameters":{"id":696,"nodeType":"ParameterList","parameters":[{"constant":false,"id":695,"mutability":"mutable","name":"_v","nameLocation":"5848:2:0","nodeType":"VariableDeclaration","scope":705,"src":"5832:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":694,"name":"string","nodeType":"ElementaryTypeName","src":"5832:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5831:20:0"},"returnParameters":{"id":697,"nodeType":"ParameterList","parameters":[],"src":"5866:0:0"},"scope":706,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[719],"contractKind":"contract","documentation":{"id":193,"nodeType":"StructuredDocumentation","src":"2415:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[706],"name":"ScriptExample","nameLocation":"2550:13:0","scope":720,"usedErrors":[]},{"id":719,"nodeType":"ContractDefinition","src":"5902:96:0","nodes":[{"id":708,"nodeType":"VariableDeclaration","src":"5924:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"5939:3:0","scope":719,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":707,"name":"uint256","nodeType":"ElementaryTypeName","src":"5924:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":718,"nodeType":"FunctionDefinition","src":"5949:47:0","nodes":[],"body":{"id":717,"nodeType":"Block","src":"5972:24:0","nodes":[],"statements":[{"expression":{"id":715,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":713,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":708,"src":"5982:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":714,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":710,"src":"5988:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"5982:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":716,"nodeType":"ExpressionStatement","src":"5982:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":711,"nodeType":"ParameterList","parameters":[{"constant":false,"id":710,"mutability":"mutable","name":"v","nameLocation":"5969:1:0","nodeType":"VariableDeclaration","scope":718,"src":"5961:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":709,"name":"uint256","nodeType":"ElementaryTypeName","src":"5961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"5960:11:0"},"returnParameters":{"id":712,"nodeType":"ParameterList","parameters":[],"src":"5972:0:0"},"scope":719,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[719],"name":"FooBar","nameLocation":"5911:6:0","scope":720,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
+{"abi":[{"type":"constructor","inputs":[{"name":"v","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"foo","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"7037:96:0:-:0;;;7084:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7117:3;:7;7037:96;;14:184:1;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;-1:-1:-1;176:16:1;;14:184;-1:-1:-1;14:184:1:o;:::-;7037:96:0;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"7037:96:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7059:18;;;;;;;;;160:25:1;;;148:2;133:18;7059::0;;;;;;","linkReferences":{}},"methodIdentifiers":{"foo()":"c2985578"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"foo\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"FooBar\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"v","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"stateMutability":"view","type":"function","name":"foo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"FooBar"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[{"astId":788,"contract":"scripts/ScriptExample.s.sol:FooBar","label":"foo","offset":0,"slot":"0","type":"t_uint256"}],"types":{"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkTester.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkTester.json
new file mode 100644
index 000000000000..94470339ecce
--- /dev/null
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkTester.json
@@ -0,0 +1 @@
+{"abi":[{"type":"function","name":"run","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5061070c806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c040622614610030575b600080fd5b61003861003a565b005b604080517f71ee464d0000000000000000000000000000000000000000000000000000000081526004810191909152600560448201527f666f726b3100000000000000000000000000000000000000000000000000000060648201526130396024820152611234908190737109709ecfa91a80626ff3989d68f67f5b1dd12d906371ee464d906084016020604051808303816000875af11580156100e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061010691906106b5565b506040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa158015610185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101a991906106ce565b67ffffffffffffffff1661303914610222576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f6e6f6e63652073686f756c64206265203132333435000000000000000000000060448201526064015b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561026d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029191906106b5565b6001146102fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f76616c75652073686f756c6420626520310000000000000000000000000000006044820152606401610219565b60018273ffffffffffffffffffffffffffffffffffffffff16311461037b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f62616c616e63652073686f756c642062652031000000000000000000000000006044820152606401610219565b604080517f71ee464d0000000000000000000000000000000000000000000000000000000081526004810191909152600560448201527f666f726b320000000000000000000000000000000000000000000000000000006064820152615ba06024820152737109709ecfa91a80626ff3989d68f67f5b1dd12d906371ee464d906084016020604051808303816000875af115801561041d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044191906106b5565b506040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa1580156104c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e491906106ce565b67ffffffffffffffff16615ba014610558576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f6e6f6e63652073686f756c6420626520313233343500000000000000000000006044820152606401610219565b8073ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c791906106b5565b600214610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f76616c75652073686f756c6420626520320000000000000000000000000000006044820152606401610219565b60028273ffffffffffffffffffffffffffffffffffffffff1631146106b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f62616c616e63652073686f756c642062652032000000000000000000000000006044820152606401610219565b5050565b6000602082840312156106c757600080fd5b5051919050565b6000602082840312156106e057600080fd5b815167ffffffffffffffff811681146106f857600080fd5b939250505056fea164736f6c634300080f000a","sourceMap":"7594:813:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c040622614610030575b600080fd5b61003861003a565b005b604080517f71ee464d0000000000000000000000000000000000000000000000000000000081526004810191909152600560448201527f666f726b3100000000000000000000000000000000000000000000000000000060648201526130396024820152611234908190737109709ecfa91a80626ff3989d68f67f5b1dd12d906371ee464d906084016020604051808303816000875af11580156100e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061010691906106b5565b506040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa158015610185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101a991906106ce565b67ffffffffffffffff1661303914610222576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f6e6f6e63652073686f756c64206265203132333435000000000000000000000060448201526064015b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561026d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029191906106b5565b6001146102fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f76616c75652073686f756c6420626520310000000000000000000000000000006044820152606401610219565b60018273ffffffffffffffffffffffffffffffffffffffff16311461037b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f62616c616e63652073686f756c642062652031000000000000000000000000006044820152606401610219565b604080517f71ee464d0000000000000000000000000000000000000000000000000000000081526004810191909152600560448201527f666f726b320000000000000000000000000000000000000000000000000000006064820152615ba06024820152737109709ecfa91a80626ff3989d68f67f5b1dd12d906371ee464d906084016020604051808303816000875af115801561041d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044191906106b5565b506040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa1580156104c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e491906106ce565b67ffffffffffffffff16615ba014610558576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f6e6f6e63652073686f756c6420626520313233343500000000000000000000006044820152606401610219565b8073ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c791906106b5565b600214610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f76616c75652073686f756c6420626520320000000000000000000000000000006044820152606401610219565b60028273ffffffffffffffffffffffffffffffffffffffff1631146106b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f62616c616e63652073686f756c642062652032000000000000000000000000006044820152606401610219565b5050565b6000602082840312156106c757600080fd5b5051919050565b6000602082840312156106e057600080fd5b815167ffffffffffffffff811681146106f857600080fd5b939250505056fea164736f6c634300080f000a","sourceMap":"7594:813:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7767:638;;;:::i;:::-;;;7909:35;;;;;;;;;238:21:1;;;;295:1;275:18;;;268:29;333:7;313:18;;;306:35;7938:5:0;393:20:1;;;386:36;7836:6:0;;;;7909:19;;;;358::1;;7909:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;7962:21:0;;;;;798:42:1;786:55;;7962:21:0;;;768:74:1;7962:11:0;;;;741:18:1;;7962:21:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;7987:5;7962:30;7954:64;;;;;;;1348:2:1;7954:64:0;;;1330:21:1;1387:2;1367:18;;;1360:30;1426:23;1406:18;;;1399:51;1467:18;;7954:64:0;;;;;;;;;8036:2;:11;;;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8053:1;8036:18;8028:48;;;;;;;1698:2:1;8028:48:0;;;1680:21:1;1737:2;1717:18;;;1710:30;1776:19;1756:18;;;1749:47;1813:18;;8028:48:0;1496:341:1;8028:48:0;8122:1;8094:8;:16;;;:30;8086:62;;;;;;;2044:2:1;8086:62:0;;;2026:21:1;2083:2;2063:18;;;2056:30;2122:21;2102:18;;;2095:49;2161:18;;8086:62:0;1842:343:1;8086:62:0;8159:35;;;;;;;;;2414:21:1;;;;2471:1;2451:18;;;2444:29;2509:7;2489:18;;;2482:35;8188:5:0;2569:20:1;;;2562:36;8159:19:0;;;;2534::1;;8159:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;8212:21:0;;;;;798:42:1;786:55;;8212:21:0;;;768:74:1;8212:11:0;;;;741:18:1;;8212:21:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;8237:5;8212:30;8204:64;;;;;;;1348:2:1;8204:64:0;;;1330:21:1;1387:2;1367:18;;;1360:30;1426:23;1406:18;;;1399:51;1467:18;;8204:64:0;1146:345:1;8204:64:0;8286:2;:11;;;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8303:1;8286:18;8278:48;;;;;;;2811:2:1;8278:48:0;;;2793:21:1;2850:2;2830:18;;;2823:30;2889:19;2869:18;;;2862:47;2926:18;;8278:48:0;2609:341:1;8278:48:0;8372:1;8344:8;:16;;;:30;8336:62;;;;;;;3157:2:1;8336:62:0;;;3139:21:1;3196:2;3176:18;;;3169:30;3235:21;3215:18;;;3208:49;3274:18;;8336:62:0;2955:343:1;8336:62:0;7791:614;;7767:638::o;433:184:1:-;503:6;556:2;544:9;535:7;531:23;527:32;524:52;;;572:1;569;562:12;524:52;-1:-1:-1;595:16:1;;433:184;-1:-1:-1;433:184:1:o;853:288::-;922:6;975:2;963:9;954:7;950:23;946:32;943:52;;;991:1;988;981:12;943:52;1023:9;1017:16;1073:18;1066:5;1062:30;1055:5;1052:41;1042:69;;1107:1;1104;1097:12;1042:69;1130:5;853:288;-1:-1:-1;;;853:288:1:o","linkReferences":{}},"methodIdentifiers":{"run()":"c0406226"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"run\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"ForkTester\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"run"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"ForkTester"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkedContract.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkedContract.json
new file mode 100644
index 000000000000..b2c69de3ffbc
--- /dev/null
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ForkedContract.json
@@ -0,0 +1 @@
+{"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getValue","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"bytecode":{"object":"0x6080604052348015600f57600080fd5b506001600055604f8060226000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632096525514602d575b600080fd5b60005460405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"7418:174:0:-:0;;;7473:36;;;;;;;;;-1:-1:-1;7501:1:0;7497;:5;7418:174;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c80632096525514602d575b600080fd5b60005460405190815260200160405180910390f3fea164736f6c634300080f000a","sourceMap":"7418:174:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7515:75;7556:7;7582:1;7515:75;;160:25:1;;;148:2;133:18;7515:75:0;;;;;;","linkReferences":{}},"methodIdentifiers":{"getValue()":"20965255"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"ForkedContract\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"stateMutability":"view","type":"function","name":"getValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"ForkedContract"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[{"astId":835,"contract":"scripts/ScriptExample.s.sol:ForkedContract","label":"v","offset":0,"slot":"0","type":"t_uint256"}],"types":{"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/NonceGetter.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/NonceGetter.json
new file mode 100644
index 000000000000..5e6afe60d710
--- /dev/null
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/NonceGetter.json
@@ -0,0 +1 @@
+{"abi":[{"type":"function","name":"getNonce","inputs":[{"name":"_addr","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5061017e806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80632d0335ab14610030575b600080fd5b61004361003e36600461010a565b610055565b60405190815260200160405180910390f35b6040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa1580156100d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fa9190610147565b67ffffffffffffffff1692915050565b60006020828403121561011c57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461014057600080fd5b9392505050565b60006020828403121561015957600080fd5b815167ffffffffffffffff8116811461014057600080fdfea164736f6c634300080f000a","sourceMap":"7135:281:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80632d0335ab14610030575b600080fd5b61004361003e36600461010a565b610055565b60405190815260200160405180910390f35b6040517f2d0335ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401602060405180830381865afa1580156100d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fa9190610147565b67ffffffffffffffff1692915050565b60006020828403121561011c57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461014057600080fd5b9392505050565b60006020828403121561015957600080fd5b815167ffffffffffffffff8116811461014057600080fdfea164736f6c634300080f000a","sourceMap":"7135:281:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7309:105;;;;;;:::i;:::-;;:::i;:::-;;;474:25:1;;;462:2;447:18;7309:105:0;;;;;;;;7389:18;;;;;686:42:1;674:55;;7389:18:0;;;656:74:1;7363:7:0;;7389:11;;;;629:18:1;;7389::0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7382:25;;;7309:105;-1:-1:-1;;7309:105:0:o;14:309:1:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;181:9;168:23;231:42;224:5;220:54;213:5;210:65;200:93;;289:1;286;279:12;200:93;312:5;14:309;-1:-1:-1;;;14:309:1:o;741:288::-;810:6;863:2;851:9;842:7;838:23;834:32;831:52;;;879:1;876;869:12;831:52;911:9;905:16;961:18;954:5;950:30;943:5;940:41;930:69;;995:1;992;985:12","linkReferences":{}},"methodIdentifiers":{"getNonce(address)":"2d0335ab"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"NonceGetter\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"stateMutability":"view","type":"function","name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"NonceGetter"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ScriptExample.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ScriptExample.json
index 09a0bf47f02d..0fd610c4725c 100644
--- a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ScriptExample.json
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/ScriptExample.json
@@ -1 +1 @@
-{"abi":[{"type":"function","name":"call1","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"call2","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"callPure","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"pure"},{"type":"function","name":"counter","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"hello","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"nested1","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"nested2","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"run","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"runBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5061202a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c8063a76ccdfa11610076578063bef03abc1161005b578063bef03abc14610111578063c040622614610119578063dbf1282f146100c357600080fd5b8063a76ccdfa146100eb578063a777d0dc146100fe57600080fd5b806361bc221a146100a85780637e79255d146100c35780637f8b915c146100d85780638d3ef7ca146100c3575b600080fd5b6100b160005481565b60405190815260200160405180910390f35b6100d66100d1366004611a60565b610121565b005b6100d66100e6366004611a60565b610178565b6100d66100f9366004611a60565b6101b7565b6100d661010c366004611a60565b61023f565b6100d66102bd565b6100d6610f54565b60008054908061013083611ad2565b919050555061017482828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b5050565b61017482828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b6000805490806101c683611ad2565b90915550506040517fdbf1282f000000000000000000000000000000000000000000000000000000008152309063dbf1282f906102099085908590600401611b31565b600060405180830381600087803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b505050505050565b61027e82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b6101746040518060400160405280601081526020017f68656c6c6f206d73672e73656e64657200000000000000000000000000000000815250336117ed565b604080518082018252600b81527f6e6f6e6365207374617274000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526103909190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab906024015b602060405180830381865afa15801561035d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103819190611b7e565b67ffffffffffffffff1661187e565b6103ce6040518060400160405280600e81526020017f74657374696e672073696e676c6500000000000000000000000000000000000081525061175b565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561043957600080fd5b505af115801561044d573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3100000000000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3200000000000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561056757600080fd5b505af115801561057b573d6000803e3d6000fd5b505050506105bd6040518060400160405280601281526020017f74657374696e672073746172742f73746f70000000000000000000000000000081525061175b565b6040517f7fec2a8d00000000000000000000000000000000000000000000000000000000815262c0ffee6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b15801561062557600080fd5b505af1158015610639573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3100000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3200000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561075357600080fd5b505af1158015610767573d6000803e3d6000fd5b50506040517f7f8b915c00000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f737461727473746f705f707572650000000000000000000000000000000000006044820152309250637f8b915c915060640160006040518083038186803b1580156107e857600080fd5b505afa1580156107fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561086b57600080fd5b505af115801561087f573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3300000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b15801561090257600080fd5b505af1158015610916573d6000803e3d6000fd5b505050506109586040518060400160405280600e81526020017f74657374696e67206e657374656400000000000000000000000000000000000081525061175b565b6040517f7fec2a8d0000000000000000000000000000000000000000000000000000000081526112346004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b1580156109bf57600080fd5b505af11580156109d3573d6000803e3d6000fd5b50506040517fa76ccdfa00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f6e65737465640000000000000000000000000000000000000000000000000000604482015230925063a76ccdfa9150606401600060405180830381600087803b158015610a5657600080fd5b505af1158015610a6a573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ad957600080fd5b505af1158015610aed573d6000803e3d6000fd5b50505050610b2f6040518060400160405280601381526020017f636f6e7472616374206465706c6f796d656e740000000000000000000000000081525061175b565b6040517fe6962cdb000000000000000000000000000000000000000000000000000000008152621234566004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b5050505060006104d2604051610bc090611a54565b908152602001604051809103906000f080158015610be2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611baf565b6104d214610c6157600080fd5b610c9f6040518060400160405280600881526020017f637265617465203200000000000000000000000000000000000000000000000081525061175b565b6040517fe6962cdb00000000000000000000000000000000000000000000000000000000815261cafe6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610d0657600080fd5b505af1158015610d1a573d6000803e3d6000fd5b505050506000602a60001b6104d2604051610d3490611a54565b9081526020018190604051809103906000f5905080158015610d5a573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dcc9190611baf565b6104d214610dd957600080fd5b610e176040518060400160405280600581526020017f646f6e652100000000000000000000000000000000000000000000000000000081525061175b565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e8257600080fd5b505af1158015610e96573d6000803e3d6000fd5b505050506104d2604051610ea990611a54565b908152602001604051809103906000f080158015610ecb573d6000803e3d6000fd5b5050604080518082018252600981527f6e6f6e636520656e640000000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526101749190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b604080517f4777f3cf0000000000000000000000000000000000000000000000000000000081526004810191909152600c60448201527f4558414d504c455f424f4f4c0000000000000000000000000000000000000000606482015260006024820181905290737109709ecfa91a80626ff3989d68f67f5b1dd12d90634777f3cf90608401602060405180830381865afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a9190611bc8565b905061105b6040518060400160405280601381526020017f626f6f6c2076616c75652066726f6d20656e76000000000000000000000000008152508261190f565b61109a6040518060400160405280600d81526020017f636f6e7472616374206164647200000000000000000000000000000000000000815250306117ed565b604080518082018252600e81527f636f6e7472616374206e6f6e6365000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526111219190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b6111606040518060400160405280600b81526020017f73656e6465722061646472000000000000000000000000000000000000000000815250336117ed565b604080518082018252600c81527f73656e646572206e6f6e63650000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523360048201526111e79190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b60408051808201825260208082527f7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d9082015290517f213e4198000000000000000000000000000000000000000000000000000000008152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063213e41989061126c908590600401611c64565b600060405180830381865afa158015611289573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526112cf9190810190611d34565b90506113456040518060400160405280600481526020017f6b657973000000000000000000000000000000000000000000000000000000008152508260008151811061131d5761131d611e68565b60200260200101518360018151811061133857611338611e68565b60200260200101516119a0565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f66726f6d206f726967696e616c000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156113c257600080fd5b505afa1580156113d6573d6000803e3d6000fd5b50506040517f06447d5600000000000000000000000000000000000000000000000000000000815260426004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d92506306447d569150602401600060405180830381600087803b15801561144057600080fd5b505af1158015611454573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b20310000000000000000000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b1580156114d557600080fd5b505afa1580156114e9573d6000803e3d6000fd5b5050505061152c6040518060400160405280601781526020017f706172656e742073636f7065206d73672e73656e646572000000000000000000815250336117ed565b61156b6040518060400160405280601a81526020017f706172656e742073636f706520636f6e74726163742e61646472000000000000815250306117ed565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b203200000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156115e857600080fd5b505afa1580156115fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166390c5013b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561166b57600080fd5b505af115801561167f573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f66726f6d206f726967696e616c20616761696e00000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b15801561170057600080fd5b505afa158015611714573d6000803e3d6000fd5b505050506117566040518060400160405280600581526020017f646f6e652100000000000000000000000000000000000000000000000000000081525061175b565b505050565b6117ea8160405160240161176f9190611e97565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f41304fac00000000000000000000000000000000000000000000000000000000179052611a2f565b50565b6101748282604051602401611803929190611eaa565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f319af33300000000000000000000000000000000000000000000000000000000179052611a2f565b6101748282604051602401611894929190611ee2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb60e72cc00000000000000000000000000000000000000000000000000000000179052611a2f565b6101748282604051602401611925929190611f04565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc3b5563500000000000000000000000000000000000000000000000000000000179052611a2f565b6117568383836040516024016119b893929190611f28565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2ced7cef000000000000000000000000000000000000000000000000000000001790525b6117ea8180516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b60b280611f6c83390190565b60008060208385031215611a7357600080fd5b823567ffffffffffffffff80821115611a8b57600080fd5b818501915085601f830112611a9f57600080fd5b813581811115611aae57600080fd5b866020828501011115611ac057600080fd5b60209290920196919550909350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611b2a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600060208284031215611b9057600080fd5b815167ffffffffffffffff81168114611ba857600080fd5b9392505050565b600060208284031215611bc157600080fd5b5051919050565b600060208284031215611bda57600080fd5b81518015158114611ba857600080fd5b60005b83811015611c05578181015183820152602001611bed565b83811115611c14576000848401525b50505050565b60008151808452611c32816020860160208601611bea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b604081526000611c776040830184611c1a565b8281036020840152600c81527f2e726f6f745f6b65795b305d000000000000000000000000000000000000000060208201526040810191505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611d2c57611d2c611cb6565b604052919050565b60006020808385031215611d4757600080fd5b825167ffffffffffffffff80821115611d5f57600080fd5b8185019150601f8681840112611d7457600080fd5b825182811115611d8657611d86611cb6565b8060051b611d95868201611ce5565b918252848101860191868101908a841115611daf57600080fd5b87870192505b83831015611e5a57825186811115611dcd5760008081fd5b8701603f81018c13611ddf5760008081fd5b88810151604088821115611df557611df5611cb6565b611e248b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a85011601611ce5565b8281528e82848601011115611e395760008081fd5b611e48838d8301848701611bea565b85525050509187019190870190611db5565b9a9950505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602081526000611ba86020830184611c1a565b604081526000611ebd6040830185611c1a565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b604081526000611ef56040830185611c1a565b90508260208301529392505050565b604081526000611f176040830185611c1a565b905082151560208301529392505050565b606081526000611f3b6060830186611c1a565b8281036020840152611f4d8186611c1a565b90508281036040840152611f618185611c1a565b969550505050505056fe608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000aa164736f6c634300080f000a","sourceMap":"2541:3359:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b50600436106100a35760003560e01c8063a76ccdfa11610076578063bef03abc1161005b578063bef03abc14610111578063c040622614610119578063dbf1282f146100c357600080fd5b8063a76ccdfa146100eb578063a777d0dc146100fe57600080fd5b806361bc221a146100a85780637e79255d146100c35780637f8b915c146100d85780638d3ef7ca146100c3575b600080fd5b6100b160005481565b60405190815260200160405180910390f35b6100d66100d1366004611a60565b610121565b005b6100d66100e6366004611a60565b610178565b6100d66100f9366004611a60565b6101b7565b6100d661010c366004611a60565b61023f565b6100d66102bd565b6100d6610f54565b60008054908061013083611ad2565b919050555061017482828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b5050565b61017482828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b6000805490806101c683611ad2565b90915550506040517fdbf1282f000000000000000000000000000000000000000000000000000000008152309063dbf1282f906102099085908590600401611b31565b600060405180830381600087803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b505050505050565b61027e82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061175b92505050565b6101746040518060400160405280601081526020017f68656c6c6f206d73672e73656e64657200000000000000000000000000000000815250336117ed565b604080518082018252600b81527f6e6f6e6365207374617274000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526103909190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab906024015b602060405180830381865afa15801561035d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103819190611b7e565b67ffffffffffffffff1661187e565b6103ce6040518060400160405280600e81526020017f74657374696e672073696e676c6500000000000000000000000000000000000081525061175b565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561043957600080fd5b505af115801561044d573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3100000000000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3200000000000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561056757600080fd5b505af115801561057b573d6000803e3d6000fd5b505050506105bd6040518060400160405280601281526020017f74657374696e672073746172742f73746f70000000000000000000000000000081525061175b565b6040517f7fec2a8d00000000000000000000000000000000000000000000000000000000815262c0ffee6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b15801561062557600080fd5b505af1158015610639573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3100000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3200000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561075357600080fd5b505af1158015610767573d6000803e3d6000fd5b50506040517f7f8b915c00000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f737461727473746f705f707572650000000000000000000000000000000000006044820152309250637f8b915c915060640160006040518083038186803b1580156107e857600080fd5b505afa1580156107fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561086b57600080fd5b505af115801561087f573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3300000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b15801561090257600080fd5b505af1158015610916573d6000803e3d6000fd5b505050506109586040518060400160405280600e81526020017f74657374696e67206e657374656400000000000000000000000000000000000081525061175b565b6040517f7fec2a8d0000000000000000000000000000000000000000000000000000000081526112346004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b1580156109bf57600080fd5b505af11580156109d3573d6000803e3d6000fd5b50506040517fa76ccdfa00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f6e65737465640000000000000000000000000000000000000000000000000000604482015230925063a76ccdfa9150606401600060405180830381600087803b158015610a5657600080fd5b505af1158015610a6a573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ad957600080fd5b505af1158015610aed573d6000803e3d6000fd5b50505050610b2f6040518060400160405280601381526020017f636f6e7472616374206465706c6f796d656e740000000000000000000000000081525061175b565b6040517fe6962cdb000000000000000000000000000000000000000000000000000000008152621234566004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b5050505060006104d2604051610bc090611a54565b908152602001604051809103906000f080158015610be2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611baf565b6104d214610c6157600080fd5b610c9f6040518060400160405280600881526020017f637265617465203200000000000000000000000000000000000000000000000081525061175b565b6040517fe6962cdb00000000000000000000000000000000000000000000000000000000815261cafe6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610d0657600080fd5b505af1158015610d1a573d6000803e3d6000fd5b505050506000602a60001b6104d2604051610d3490611a54565b9081526020018190604051809103906000f5905080158015610d5a573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dcc9190611baf565b6104d214610dd957600080fd5b610e176040518060400160405280600581526020017f646f6e652100000000000000000000000000000000000000000000000000000081525061175b565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e8257600080fd5b505af1158015610e96573d6000803e3d6000fd5b505050506104d2604051610ea990611a54565b908152602001604051809103906000f080158015610ecb573d6000803e3d6000fd5b5050604080518082018252600981527f6e6f6e636520656e640000000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526101749190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b604080517f4777f3cf0000000000000000000000000000000000000000000000000000000081526004810191909152600c60448201527f4558414d504c455f424f4f4c0000000000000000000000000000000000000000606482015260006024820181905290737109709ecfa91a80626ff3989d68f67f5b1dd12d90634777f3cf90608401602060405180830381865afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a9190611bc8565b905061105b6040518060400160405280601381526020017f626f6f6c2076616c75652066726f6d20656e76000000000000000000000000008152508261190f565b61109a6040518060400160405280600d81526020017f636f6e7472616374206164647200000000000000000000000000000000000000815250306117ed565b604080518082018252600e81527f636f6e7472616374206e6f6e6365000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526111219190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b6111606040518060400160405280600b81526020017f73656e6465722061646472000000000000000000000000000000000000000000815250336117ed565b604080518082018252600c81527f73656e646572206e6f6e63650000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523360048201526111e79190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b60408051808201825260208082527f7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d9082015290517f213e4198000000000000000000000000000000000000000000000000000000008152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063213e41989061126c908590600401611c64565b600060405180830381865afa158015611289573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526112cf9190810190611d34565b90506113456040518060400160405280600481526020017f6b657973000000000000000000000000000000000000000000000000000000008152508260008151811061131d5761131d611e68565b60200260200101518360018151811061133857611338611e68565b60200260200101516119a0565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f66726f6d206f726967696e616c000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156113c257600080fd5b505afa1580156113d6573d6000803e3d6000fd5b50506040517f06447d5600000000000000000000000000000000000000000000000000000000815260426004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d92506306447d569150602401600060405180830381600087803b15801561144057600080fd5b505af1158015611454573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b20310000000000000000000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b1580156114d557600080fd5b505afa1580156114e9573d6000803e3d6000fd5b5050505061152c6040518060400160405280601781526020017f706172656e742073636f7065206d73672e73656e646572000000000000000000815250336117ed565b61156b6040518060400160405280601a81526020017f706172656e742073636f706520636f6e74726163742e61646472000000000000815250306117ed565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b203200000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156115e857600080fd5b505afa1580156115fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166390c5013b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561166b57600080fd5b505af115801561167f573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f66726f6d206f726967696e616c20616761696e00000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b15801561170057600080fd5b505afa158015611714573d6000803e3d6000fd5b505050506117566040518060400160405280600581526020017f646f6e652100000000000000000000000000000000000000000000000000000081525061175b565b505050565b6117ea8160405160240161176f9190611e97565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f41304fac00000000000000000000000000000000000000000000000000000000179052611a2f565b50565b6101748282604051602401611803929190611eaa565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f319af33300000000000000000000000000000000000000000000000000000000179052611a2f565b6101748282604051602401611894929190611ee2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb60e72cc00000000000000000000000000000000000000000000000000000000179052611a2f565b6101748282604051602401611925929190611f04565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc3b5563500000000000000000000000000000000000000000000000000000000179052611a2f565b6117568383836040516024016119b893929190611f28565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2ced7cef000000000000000000000000000000000000000000000000000000001790525b6117ea8180516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b60b280611f6c83390190565b60008060208385031215611a7357600080fd5b823567ffffffffffffffff80821115611a8b57600080fd5b818501915085601f830112611a9f57600080fd5b813581811115611aae57600080fd5b866020828501011115611ac057600080fd5b60209290920196919550909350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611b2a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600060208284031215611b9057600080fd5b815167ffffffffffffffff81168114611ba857600080fd5b9392505050565b600060208284031215611bc157600080fd5b5051919050565b600060208284031215611bda57600080fd5b81518015158114611ba857600080fd5b60005b83811015611c05578181015183820152602001611bed565b83811115611c14576000848401525b50505050565b60008151808452611c32816020860160208601611bea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b604081526000611c776040830184611c1a565b8281036020840152600c81527f2e726f6f745f6b65795b305d000000000000000000000000000000000000000060208201526040810191505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611d2c57611d2c611cb6565b604052919050565b60006020808385031215611d4757600080fd5b825167ffffffffffffffff80821115611d5f57600080fd5b8185019150601f8681840112611d7457600080fd5b825182811115611d8657611d86611cb6565b8060051b611d95868201611ce5565b918252848101860191868101908a841115611daf57600080fd5b87870192505b83831015611e5a57825186811115611dcd5760008081fd5b8701603f81018c13611ddf5760008081fd5b88810151604088821115611df557611df5611cb6565b611e248b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a85011601611ce5565b8281528e82848601011115611e395760008081fd5b611e48838d8301848701611bea565b85525050509187019190870190611db5565b9a9950505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602081526000611ba86020830184611c1a565b604081526000611ebd6040830185611c1a565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b604081526000611ef56040830185611c1a565b90508260208301529392505050565b604081526000611f176040830185611c1a565b905082151560208301529392505050565b606081526000611f3b6060830186611c1a565b8281036020840152611f4d8186611c1a565b90508281036040840152611f618185611c1a565b969550505050505056fe608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000aa164736f6c634300080f000a","sourceMap":"2541:3359:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2775:22;;;;;;;;;160:25:1;;;148:2;133:18;2775:22:0;;;;;;;5405:95;;;;;;:::i;:::-;;:::i;:::-;;5814:84;;;;;;:::i;:::-;;:::i;5607:98::-;;;;;;:::i;:::-;;:::i;5256:143::-;;;;;;:::i;:::-;;:::i;3903:1258::-;;;:::i;2887:949::-;;;:::i;5405:95::-;5459:7;:9;;;:7;:9;;;:::i;:::-;;;;;;5478:15;5490:2;;5478:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5478:11:0;;-1:-1:-1;;;5478:15:0:i;:::-;5405:95;;:::o;5814:84::-;5876:15;5888:2;;5876:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5876:11:0;;-1:-1:-1;;;5876:15:0:i;5607:98::-;5663:7;:9;;;:7;:9;;;:::i;:::-;;;;-1:-1:-1;;5682:16:0;;;;;:4;;:12;;:16;;5695:2;;;;5682:16;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5607:98;;:::o;5256:143::-;5315:15;5327:2;;5315:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5315:11:0;;-1:-1:-1;;;5315:15:0:i;:::-;5340:52;;;;;;;;;;;;;;;;;;5380:10;5340:11;:52::i;3903:1258::-;3944:63;;;;;;;;;;;;;;;;3979:26;;;;;3999:4;3979:26;;;1747:74:1;3944:63:0;;;3979:11;;;;1720:18:1;;3979:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3971:35;;3944:11;:63::i;:::-;4018:29;;;;;;;;;;;;;;;;;;:11;:29::i;:::-;2634:28;2626:37;;4057:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4081:26:0;;;;;2327:2:1;4081:26:0;;;2309:21:1;2366:2;2346:18;;;2339:30;2405:14;2385:18;;;2378:42;4081:4:0;;-1:-1:-1;4081:10:0;;-1:-1:-1;2437:18:1;;4081:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4117:26:0;;;;;2668:2:1;4117:26:0;;;2650:21:1;2707:2;2687:18;;;2680:30;2746:14;2726:18;;;2719:42;4117:4:0;;-1:-1:-1;4117:10:0;;-1:-1:-1;2778:18:1;;4117:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4154:33;;;;;;;;;;;;;;;;;;:11;:33::i;:::-;4197:45;;;;;4231:8;4197:45;;;1747:74:1;4197:17:0;;;;1720:18:1;;4197:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4252:29:0;;;;;3009:2:1;4252:29:0;;;2991:21:1;3048:2;3028:18;;;3021:30;3087:17;3067:18;;;3060:45;4252:4:0;;-1:-1:-1;4252:10:0;;-1:-1:-1;3122:18:1;;4252:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4291:29:0;;;;;3353:2:1;4291:29:0;;;3335:21:1;3392:2;3372:18;;;3365:30;3431:17;3411:18;;;3404:45;4291:4:0;;-1:-1:-1;4291:10:0;;-1:-1:-1;3466:18:1;;4291:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4330:31:0;;;;;3697:2:1;4330:31:0;;;3679:21:1;3736:2;3716:18;;;3709:30;3775:16;3755:18;;;3748:44;4330:4:0;;-1:-1:-1;4330:13:0;;-1:-1:-1;3809:18:1;;4330:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2634:28;2626:37;;4371:16;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4399:29:0;;;;;4040:2:1;4399:29:0;;;4022:21:1;4079:2;4059:18;;;4052:30;4118:17;4098:18;;;4091:45;4399:4:0;;-1:-1:-1;4399:10:0;;-1:-1:-1;4153:18:1;;4399:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4439;;;;;;;;;;;;;;;;;;:11;:29::i;:::-;4478:43;;;;;4512:6;4478:43;;;1747:74:1;4478:17:0;;;;1720:18:1;;4478:43:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4531:22:0;;;;;4384:2:1;4531:22:0;;;4366:21:1;4423:1;4403:18;;;4396:29;4461:8;4441:18;;;4434:36;4531:4:0;;-1:-1:-1;4531:12:0;;-1:-1:-1;4487:18:1;;4531:22:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2634:28;2626:37;;4563:16;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4592:34;;;;;;;;;;;;;;;;;;:11;:34::i;:::-;4636:40;;;;;4665:8;4636:40;;;1747:74:1;4636:12:0;;;;1720:18:1;;4636:40:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4686:8;4708:4;4697:16;;;;;:::i;:::-;160:25:1;;;148:2;133:18;4697:16:0;;;;;;;;;;;;;;;;;;;;;;;4686:27;;4731:1;:5;;;:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4742:4;4731:15;4723:24;;;;;;4758:23;;;;;;;;;;;;;;;;;;:11;:23::i;:::-;4791:38;;;;;4820:6;4791:38;;;1747:74:1;4791:12:0;;;;1720:18:1;;4791:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4839:8;4883:2;4867:20;;4889:4;4850:44;;;;;:::i;:::-;160:25:1;;;148:2;133:18;4850:44:0;;;;;;;;;;;;;;;;;;;;;;;;;;;4839:55;;4912:1;:5;;;:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4923:4;4912:15;4904:24;;;;;;4938:20;;;;;;;;;;;;;;;;;;:11;:20::i;:::-;2634:28;2626:37;;5042:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5077:4;5066:16;;;;;:::i;:::-;160:25:1;;;148:2;133:18;5066:16:0;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5093:61:0;;;;;;;;;;;;;;;;5126:26;;;;;5146:4;5126:26;;;1747:74:1;5093:61:0;;;5126:11;;;;1720:18:1;;5126:26:0;1601:226:1;2887:949:0;2928:31;;;;;;;;;5104:21:1;;;;5161:2;5141:18;;;5134:30;5200:14;5180:18;;;5173:42;2919:6:0;5267:20:1;;;5260:52;;;2919:6:0;2928:8;;;;5232:19:1;;2928:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2919:40;;2969:37;;;;;;;;;;;;;;;;;;3004:1;2969:11;:37::i;:::-;3017:43;;;;;;;;;;;;;;;;;;3054:4;3017:11;:43::i;:::-;3070:57;;;;;;;;;;;;;;;;3100:26;;;;;3120:4;3100:26;;;1747:74:1;3070:57:0;;;3100:11;;;;1720:18:1;;3100:26:0;1601:226:1;3070:57:0;3137:47;;;;;;;;;;;;;;;;;;3172:10;3137:11;:47::i;:::-;3194:61;;;;;;;;;;;;;;;;3222:32;;;;;3242:10;3222:32;;;1747:74:1;3194:61:0;;;3222:11;;;;1720:18:1;;3222:32:0;1601:226:1;3194:61:0;3266:55;;;;;;;;;;;;;;;;;3354:38;;;;;3266:18;;3354:16;;;;:38;;3266:55;;3354:38;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3331:61;;3402:37;;;;;;;;;;;;;;;;;;3422:4;3427:1;3422:7;;;;;;;;:::i;:::-;;;;;;;3431:4;3436:1;3431:7;;;;;;;;:::i;:::-;;;;;;;3402:11;:37::i;:::-;3450:27;;;;;9413:2:1;3450:27:0;;;9395:21:1;9452:2;9432:18;;;9425:30;9491:15;9471:18;;;9464:43;3450:4:0;;:10;;9524:18:1;;3450:27:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3487:37:0;;;;;3517:4;3487:37;;;1747:74:1;3487:13:0;;-1:-1:-1;3487:13:0;;-1:-1:-1;1720:18:1;;3487:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3534:26:0;;;;;9755:2:1;3534:26:0;;;9737:21:1;9794:2;9774:18;;;9767:30;9833:14;9813:18;;;9806:42;3534:4:0;;-1:-1:-1;3534:10:0;;-1:-1:-1;9865:18:1;;3534:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3570:59;;;;;;;;;;;;;;;;;;3617:10;3570:11;:59::i;:::-;3639:56;;;;;;;;;;;;;;;;;;3689:4;3639:11;:56::i;:::-;3705:26;;;;;10096:2:1;3705:26:0;;;10078:21:1;10135:2;10115:18;;;10108:30;10174:14;10154:18;;;10147:42;3705:4:0;;:10;;10206:18:1;;3705:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2634:28;2626:37;;3741:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3765:33:0;;;;;10437:2:1;3765:33:0;;;10419:21:1;10476:2;10456:18;;;10449:30;10515:21;10495:18;;;10488:49;3765:4:0;;-1:-1:-1;3765:10:0;;-1:-1:-1;10554:18:1;;3765:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3809:20;;;;;;;;;;;;;;;;;;:11;:20::i;:::-;2909:927;;;2887:949::o;1658:121::-;1713:59;1768:2;1729:42;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1713:15;:59::i;:::-;1658:121;:::o;2081:145::-;2148:71;2211:2;2215;2164:54;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2148:15;:71::i;1930:145::-;1997:71;2060:2;2064;2013:54;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1997:15;:71::i;1785:139::-;1849:68;1909:2;1913;1865:51;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1849:15;:68::i;2232:179::-;2323:81;2392:2;2396;2400;2339:64;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1133:133;1204:55;1251:7;1370:14;;856:42;1543:2;1530:16;;1346:21;;1370:14;1530:16;856:42;1579:5;1568:68;1559:77;;1496:150;;1272:380;:::o;-1:-1:-1:-;;;;;;;;:::o;196:592:1:-;267:6;275;328:2;316:9;307:7;303:23;299:32;296:52;;;344:1;341;334:12;296:52;384:9;371:23;413:18;454:2;446:6;443:14;440:34;;;470:1;467;460:12;440:34;508:6;497:9;493:22;483:32;;553:7;546:4;542:2;538:13;534:27;524:55;;575:1;572;565:12;524:55;615:2;602:16;641:2;633:6;630:14;627:34;;;657:1;654;647:12;627:34;702:7;697:2;688:6;684:2;680:15;676:24;673:37;670:57;;;723:1;720;713:12;670:57;754:2;746:11;;;;;776:6;;-1:-1:-1;196:592:1;;-1:-1:-1;;;;196:592:1:o;793:349::-;832:3;863:66;856:5;853:77;850:257;;963:77;960:1;953:88;1064:4;1061:1;1054:15;1092:4;1089:1;1082:15;850:257;-1:-1:-1;1134:1:1;1123:13;;793:349::o;1147:449::-;1306:2;1295:9;1288:21;1345:6;1340:2;1329:9;1325:18;1318:34;1402:6;1394;1389:2;1378:9;1374:18;1361:48;1458:1;1429:22;;;1453:2;1425:31;;;1418:42;;;;1512:2;1500:15;;;1517:66;1496:88;1481:104;1477:113;;1147:449;-1:-1:-1;1147:449:1:o;1832:288::-;1901:6;1954:2;1942:9;1933:7;1929:23;1925:32;1922:52;;;1970:1;1967;1960:12;1922:52;2002:9;1996:16;2052:18;2045:5;2041:30;2034:5;2031:41;2021:69;;2086:1;2083;2076:12;2021:69;2109:5;1832:288;-1:-1:-1;;;1832:288:1:o;4709:184::-;4779:6;4832:2;4820:9;4811:7;4807:23;4803:32;4800:52;;;4848:1;4845;4838:12;4800:52;-1:-1:-1;4871:16:1;;4709:184;-1:-1:-1;4709:184:1:o;5323:277::-;5390:6;5443:2;5431:9;5422:7;5418:23;5414:32;5411:52;;;5459:1;5456;5449:12;5411:52;5491:9;5485:16;5544:5;5537:13;5530:21;5523:5;5520:32;5510:60;;5566:1;5563;5556:12;5605:258;5677:1;5687:113;5701:6;5698:1;5695:13;5687:113;;;5777:11;;;5771:18;5758:11;;;5751:39;5723:2;5716:10;5687:113;;;5818:6;5815:1;5812:13;5809:48;;;5853:1;5844:6;5839:3;5835:16;5828:27;5809:48;;5605:258;;;:::o;5868:317::-;5910:3;5948:5;5942:12;5975:6;5970:3;5963:19;5991:63;6047:6;6040:4;6035:3;6031:14;6024:4;6017:5;6013:16;5991:63;:::i;:::-;6099:2;6087:15;6104:66;6083:88;6074:98;;;;6174:4;6070:109;;5868:317;-1:-1:-1;;5868:317:1:o;6190:493::-;6440:2;6429:9;6422:21;6403:4;6466:45;6507:2;6496:9;6492:18;6484:6;6466:45;:::i;:::-;6559:9;6551:6;6547:22;6542:2;6531:9;6527:18;6520:50;6594:2;6586:6;6579:18;6630:14;6625:2;6617:6;6613:15;6606:39;6674:2;6666:6;6662:15;6654:23;;;6190:493;;;;:::o;6688:184::-;6740:77;6737:1;6730:88;6837:4;6834:1;6827:15;6861:4;6858:1;6851:15;6877:334;6948:2;6942:9;7004:2;6994:13;;7009:66;6990:86;6978:99;;7107:18;7092:34;;7128:22;;;7089:62;7086:88;;;7154:18;;:::i;:::-;7190:2;7183:22;6877:334;;-1:-1:-1;6877:334:1:o;7216:1801::-;7321:6;7352:2;7395;7383:9;7374:7;7370:23;7366:32;7363:52;;;7411:1;7408;7401:12;7363:52;7444:9;7438:16;7473:18;7514:2;7506:6;7503:14;7500:34;;;7530:1;7527;7520:12;7500:34;7568:6;7557:9;7553:22;7543:32;;7594:4;7634:7;7629:2;7625;7621:11;7617:25;7607:53;;7656:1;7653;7646:12;7607:53;7685:2;7679:9;7707:2;7703;7700:10;7697:36;;;7713:18;;:::i;:::-;7759:2;7756:1;7752:10;7782:28;7806:2;7802;7798:11;7782:28;:::i;:::-;7844:15;;;7914:11;;;7910:20;;;7875:12;;;;7942:19;;;7939:39;;;7974:1;7971;7964:12;7939:39;8006:2;8002;7998:11;7987:22;;8018:969;8034:6;8029:3;8026:15;8018:969;;;8113:3;8107:10;8149:2;8136:11;8133:19;8130:109;;;8193:1;8222:2;8218;8211:14;8130:109;8262:20;;8317:2;8309:11;;8305:25;-1:-1:-1;8295:123:1;;8372:1;8401:2;8397;8390:14;8295:123;8456:2;8452;8448:11;8442:18;8484:2;8510;8505:3;8502:11;8499:37;;;8516:18;;:::i;:::-;8562:111;8669:2;8600:66;8595:2;8590:3;8586:12;8582:85;8578:94;8562:111;:::i;:::-;8700:3;8693:5;8686:18;8747:7;8741:3;8735;8731:2;8727:12;8723:22;8720:35;8717:128;;;8797:1;8827:3;8822;8815:16;8717:128;8858:56;8910:3;8905:2;8898:5;8894:14;8888:3;8884:2;8880:12;8858:56;:::i;:::-;8927:18;;-1:-1:-1;;;8051:12:1;;;;8965;;;;8018:969;;;9006:5;7216:1801;-1:-1:-1;;;;;;;;;;7216:1801:1:o;9022:184::-;9074:77;9071:1;9064:88;9171:4;9168:1;9161:15;9195:4;9192:1;9185:15;10583:220;10732:2;10721:9;10714:21;10695:4;10752:45;10793:2;10782:9;10778:18;10770:6;10752:45;:::i;10808:340::-;10985:2;10974:9;10967:21;10948:4;11005:45;11046:2;11035:9;11031:18;11023:6;11005:45;:::i;:::-;10997:53;;11098:42;11090:6;11086:55;11081:2;11070:9;11066:18;11059:83;10808:340;;;;;:::o;11153:291::-;11330:2;11319:9;11312:21;11293:4;11350:45;11391:2;11380:9;11376:18;11368:6;11350:45;:::i;:::-;11342:53;;11431:6;11426:2;11415:9;11411:18;11404:34;11153:291;;;;;:::o;11449:301::-;11620:2;11609:9;11602:21;11583:4;11640:45;11681:2;11670:9;11666:18;11658:6;11640:45;:::i;:::-;11632:53;;11735:6;11728:14;11721:22;11716:2;11705:9;11701:18;11694:50;11449:301;;;;;:::o;11755:546::-;12000:2;11989:9;11982:21;11963:4;12026:45;12067:2;12056:9;12052:18;12044:6;12026:45;:::i;:::-;12119:9;12111:6;12107:22;12102:2;12091:9;12087:18;12080:50;12153:33;12179:6;12171;12153:33;:::i;:::-;12139:47;;12234:9;12226:6;12222:22;12217:2;12206:9;12202:18;12195:50;12262:33;12288:6;12280;12262:33;:::i;:::-;12254:41;11755:546;-1:-1:-1;;;;;;11755:546:1:o","linkReferences":{}},"methodIdentifiers":{"call1(string)":"7e79255d","call2(string)":"8d3ef7ca","callPure(string)":"7f8b915c","counter()":"61bc221a","hello(string)":"a777d0dc","nested1(string)":"a76ccdfa","nested2(string)":"dbf1282f","run()":"c0406226","runBroadcast()":"bef03abc"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"call1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"call2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"callPure\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"hello\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"nested1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"nested2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"run\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"runBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"ScriptExample\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"hello(string)\":{\"notice\":\"example external function, to force a CALL, and test vm.startPrank with.\"},\"run()\":{\"notice\":\"example function, runs through basic cheat-codes and console logs.\"},\"runBroadcast()\":{\"notice\":\"example function, to test vm.broadcast with.\"}},\"notice\":\"ScriptExample is an example script. The Go forge script code tests that it can run this.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"ScriptExample\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3\",\"dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"call1"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"call2"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"pure","type":"function","name":"callPure"},{"inputs":[],"stateMutability":"view","type":"function","name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"view","type":"function","name":"hello"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"nested1"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"nested2"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"run"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"runBroadcast"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{"hello(string)":{"notice":"example external function, to force a CALL, and test vm.startPrank with."},"run()":{"notice":"example function, runs through basic cheat-codes and console logs."},"runBroadcast()":{"notice":"example function, to test vm.broadcast with."}},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"ScriptExample"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d","urls":["bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3","dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[{"astId":215,"contract":"scripts/ScriptExample.s.sol:ScriptExample","label":"counter","offset":0,"slot":"0","type":"t_uint256"}],"types":{"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}},"userdoc":{"version":1,"kind":"user","methods":{"hello(string)":{"notice":"example external function, to force a CALL, and test vm.startPrank with."},"run()":{"notice":"example function, runs through basic cheat-codes and console logs."},"runBroadcast()":{"notice":"example function, to test vm.broadcast with."}},"notice":"ScriptExample is an example script. The Go forge script code tests that it can run this."},"devdoc":{"version":1,"kind":"dev","title":"ScriptExample"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":720,"exportedSymbols":{"FooBar":[719],"ScriptExample":[706],"Vm":[55],"console":[192]},"nodeType":"SourceUnit","src":"32:5967:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":55,"nodeType":"ContractDefinition","src":"120:616:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":55,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[55],"name":"Vm","nameLocation":"130:2:0","scope":720,"usedErrors":[]},{"id":192,"nodeType":"ContractDefinition","src":"791:1622:0","nodes":[{"id":61,"nodeType":"VariableDeclaration","src":"813:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"830:15:0","scope":192,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":56,"name":"address","nodeType":"ElementaryTypeName","src":"813:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":59,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"856:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":58,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"848:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":57,"name":"address","nodeType":"ElementaryTypeName","src":"848:7:0","typeDescriptions":{}}},"id":60,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"848:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":78,"nodeType":"FunctionDefinition","src":"906:221:0","nodes":[],"body":{"id":77,"nodeType":"Block","src":"1065:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1084:37:0","statements":[{"nodeType":"YulAssignment","src":"1098:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1107:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1098:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":67,"isOffset":false,"isSlot":false,"src":"1107:4:0","valueSize":1},{"declaration":74,"isOffset":false,"isSlot":false,"src":"1098:5:0","valueSize":1}],"id":76,"nodeType":"InlineAssembly","src":"1075:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"915:25:0","parameters":{"id":68,"nodeType":"ParameterList","parameters":[{"constant":false,"id":67,"mutability":"mutable","name":"fnIn","nameLocation":"987:4:0","nodeType":"VariableDeclaration","scope":78,"src":"950:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":66,"nodeType":"FunctionTypeName","parameterTypes":{"id":64,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":66,"src":"959:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":62,"name":"bytes","nodeType":"ElementaryTypeName","src":"959:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"958:14:0"},"returnParameterTypes":{"id":65,"nodeType":"ParameterList","parameters":[],"src":"987:0:0"},"src":"950:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"940:57:0"},"returnParameters":{"id":75,"nodeType":"ParameterList","parameters":[{"constant":false,"id":74,"mutability":"mutable","name":"fnOut","nameLocation":"1058:5:0","nodeType":"VariableDeclaration","scope":78,"src":"1021:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":73,"nodeType":"FunctionTypeName","parameterTypes":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":73,"src":"1030:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":69,"name":"bytes","nodeType":"ElementaryTypeName","src":"1030:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1029:14:0"},"returnParameterTypes":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"1058:0:0"},"src":"1021:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1020:44:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":90,"nodeType":"FunctionDefinition","src":"1133:133:0","nodes":[],"body":{"id":89,"nodeType":"Block","src":"1194:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":86,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":80,"src":"1251:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":84,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1230:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":83,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":78,"src":"1204:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":85,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":87,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":88,"nodeType":"ExpressionStatement","src":"1204:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1142:15:0","parameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"payload","nameLocation":"1171:7:0","nodeType":"VariableDeclaration","scope":90,"src":"1158:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":79,"name":"bytes","nodeType":"ElementaryTypeName","src":"1158:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1157:22:0"},"returnParameters":{"id":82,"nodeType":"ParameterList","parameters":[],"src":"1194:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1272:380:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1336:316:0","nodes":[],"statements":[{"assignments":[96],"declarations":[{"constant":false,"id":96,"mutability":"mutable","name":"payloadLength","nameLocation":"1354:13:0","nodeType":"VariableDeclaration","scope":105,"src":"1346:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":95,"name":"uint256","nodeType":"ElementaryTypeName","src":"1346:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":99,"initialValue":{"expression":{"id":97,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":92,"src":"1370:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":98,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1370:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1346:38:0"},{"assignments":[101],"declarations":[{"constant":false,"id":101,"mutability":"mutable","name":"consoleAddress","nameLocation":"1402:14:0","nodeType":"VariableDeclaration","scope":105,"src":"1394:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":100,"name":"address","nodeType":"ElementaryTypeName","src":"1394:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":103,"initialValue":{"id":102,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":61,"src":"1419:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1394:40:0"},{"AST":{"nodeType":"YulBlock","src":"1496:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1510:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1534:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1543:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1530:3:0"},"nodeType":"YulFunctionCall","src":"1530:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1514:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1559:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1579:3:0"},"nodeType":"YulFunctionCall","src":"1579:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1586:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1602:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1616:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1631:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1634:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1568:10:0"},"nodeType":"YulFunctionCall","src":"1568:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1563:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":101,"isOffset":false,"isSlot":false,"src":"1586:14:0","valueSize":1},{"declaration":92,"isOffset":false,"isSlot":false,"src":"1534:7:0","valueSize":1},{"declaration":96,"isOffset":false,"isSlot":false,"src":"1616:13:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1487:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1281:19:0","parameters":{"id":93,"nodeType":"ParameterList","parameters":[{"constant":false,"id":92,"mutability":"mutable","name":"payload","nameLocation":"1314:7:0","nodeType":"VariableDeclaration","scope":106,"src":"1301:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":91,"name":"bytes","nodeType":"ElementaryTypeName","src":"1301:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1300:22:0"},"returnParameters":{"id":94,"nodeType":"ParameterList","parameters":[],"src":"1336:0:0"},"scope":192,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":120,"nodeType":"FunctionDefinition","src":"1658:121:0","nodes":[],"body":{"id":119,"nodeType":"Block","src":"1703:76:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":114,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1753:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":115,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1768:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":112,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1729:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":113,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1729:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":116,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1729:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":111,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1713:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":117,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1713:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":118,"nodeType":"ExpressionStatement","src":"1713:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1667:3:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"p0","nameLocation":"1685:2:0","nodeType":"VariableDeclaration","scope":120,"src":"1671:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":107,"name":"string","nodeType":"ElementaryTypeName","src":"1671:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1670:18:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":137,"nodeType":"FunctionDefinition","src":"1785:139:0","nodes":[],"body":{"id":136,"nodeType":"Block","src":"1839:85:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":130,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1889:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":131,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":122,"src":"1909:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":132,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":124,"src":"1913:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":128,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1865:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":129,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1865:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":133,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1865:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":127,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1849:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":134,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1849:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":135,"nodeType":"ExpressionStatement","src":"1849:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1794:3:0","parameters":{"id":125,"nodeType":"ParameterList","parameters":[{"constant":false,"id":122,"mutability":"mutable","name":"p0","nameLocation":"1812:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1798:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":121,"name":"string","nodeType":"ElementaryTypeName","src":"1798:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":124,"mutability":"mutable","name":"p1","nameLocation":"1821:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1816:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":123,"name":"bool","nodeType":"ElementaryTypeName","src":"1816:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"1797:27:0"},"returnParameters":{"id":126,"nodeType":"ParameterList","parameters":[],"src":"1839:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":154,"nodeType":"FunctionDefinition","src":"1930:145:0","nodes":[],"body":{"id":153,"nodeType":"Block","src":"1987:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":147,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2037:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":148,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":139,"src":"2060:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":149,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":141,"src":"2064:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":145,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2013:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":146,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2013:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":150,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2013:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":144,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1997:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":151,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1997:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":152,"nodeType":"ExpressionStatement","src":"1997:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1939:3:0","parameters":{"id":142,"nodeType":"ParameterList","parameters":[{"constant":false,"id":139,"mutability":"mutable","name":"p0","nameLocation":"1957:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1943:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":138,"name":"string","nodeType":"ElementaryTypeName","src":"1943:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":141,"mutability":"mutable","name":"p1","nameLocation":"1969:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1961:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":140,"name":"uint256","nodeType":"ElementaryTypeName","src":"1961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1942:30:0"},"returnParameters":{"id":143,"nodeType":"ParameterList","parameters":[],"src":"1987:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":171,"nodeType":"FunctionDefinition","src":"2081:145:0","nodes":[],"body":{"id":170,"nodeType":"Block","src":"2138:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":164,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2188:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":165,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":156,"src":"2211:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":166,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":158,"src":"2215:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":162,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2164:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":163,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2164:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":167,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2164:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":161,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2148:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":168,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2148:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":169,"nodeType":"ExpressionStatement","src":"2148:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2090:3:0","parameters":{"id":159,"nodeType":"ParameterList","parameters":[{"constant":false,"id":156,"mutability":"mutable","name":"p0","nameLocation":"2108:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2094:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":155,"name":"string","nodeType":"ElementaryTypeName","src":"2094:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":158,"mutability":"mutable","name":"p1","nameLocation":"2120:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2112:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":157,"name":"address","nodeType":"ElementaryTypeName","src":"2112:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2093:30:0"},"returnParameters":{"id":160,"nodeType":"ParameterList","parameters":[],"src":"2138:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":191,"nodeType":"FunctionDefinition","src":"2232:179:0","nodes":[],"body":{"id":190,"nodeType":"Block","src":"2313:98:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":183,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2363:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":184,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":173,"src":"2392:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":185,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":175,"src":"2396:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":186,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":177,"src":"2400:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":181,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2339:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":182,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2339:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":187,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2339:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":180,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2323:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":188,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2323:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":189,"nodeType":"ExpressionStatement","src":"2323:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2241:3:0","parameters":{"id":178,"nodeType":"ParameterList","parameters":[{"constant":false,"id":173,"mutability":"mutable","name":"p0","nameLocation":"2259:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2245:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":172,"name":"string","nodeType":"ElementaryTypeName","src":"2245:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":175,"mutability":"mutable","name":"p1","nameLocation":"2277:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2263:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":174,"name":"string","nodeType":"ElementaryTypeName","src":"2263:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":177,"mutability":"mutable","name":"p2","nameLocation":"2295:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2281:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":176,"name":"string","nodeType":"ElementaryTypeName","src":"2281:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2244:54:0"},"returnParameters":{"id":179,"nodeType":"ParameterList","parameters":[],"src":"2313:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[192],"name":"console","nameLocation":"799:7:0","scope":720,"usedErrors":[]},{"id":706,"nodeType":"ContractDefinition","src":"2541:3359:0","nodes":[{"id":207,"nodeType":"VariableDeclaration","src":"2571:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"2597:10:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":194,"name":"address","nodeType":"ElementaryTypeName","src":"2571:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":202,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2644:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":201,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"2634:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":203,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2634:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":200,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2626:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":199,"name":"uint256","nodeType":"ElementaryTypeName","src":"2626:7:0","typeDescriptions":{}}},"id":204,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2626:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":198,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2618:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":197,"name":"uint160","nodeType":"ElementaryTypeName","src":"2618:7:0","typeDescriptions":{}}},"id":205,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2618:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":196,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2610:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":195,"name":"address","nodeType":"ElementaryTypeName","src":"2610:7:0","typeDescriptions":{}}},"id":206,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2610:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":213,"nodeType":"VariableDeclaration","src":"2671:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"2692:2:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"},"typeName":{"id":209,"nodeType":"UserDefinedTypeName","pathNode":{"id":208,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":55,"src":"2671:2:0"},"referencedDeclaration":55,"src":"2671:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"value":{"arguments":[{"id":211,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":207,"src":"2700:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":210,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":55,"src":"2697:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$55_$","typeString":"type(contract Vm)"}},"id":212,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2697:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"visibility":"internal"},{"id":215,"nodeType":"VariableDeclaration","src":"2775:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"2790:7:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":214,"name":"uint256","nodeType":"ElementaryTypeName","src":"2775:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":378,"nodeType":"FunctionDefinition","src":"2887:949:0","nodes":[],"body":{"id":377,"nodeType":"Block","src":"2909:927:0","nodes":[],"statements":[{"assignments":[220],"declarations":[{"constant":false,"id":220,"mutability":"mutable","name":"x","nameLocation":"2924:1:0","nodeType":"VariableDeclaration","scope":377,"src":"2919:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":219,"name":"bool","nodeType":"ElementaryTypeName","src":"2919:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":226,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":223,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2937:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":224,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"2953:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":221,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"2928:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":222,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"2928:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":225,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2928:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"2919:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2981:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":231,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3004:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":227,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"2969:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":229,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":137,"src":"2969:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":232,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2969:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":233,"nodeType":"ExpressionStatement","src":"2969:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":237,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3029:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":240,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3054:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":239,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3046:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":238,"name":"address","nodeType":"ElementaryTypeName","src":"3046:7:0","typeDescriptions":{}}},"id":241,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3046:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":234,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3017:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":236,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3017:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":242,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3017:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":243,"nodeType":"ExpressionStatement","src":"3017:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":247,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3082:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":252,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3120:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":251,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3112:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":250,"name":"address","nodeType":"ElementaryTypeName","src":"3112:7:0","typeDescriptions":{}}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3112:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":248,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3100:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":249,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3100:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":254,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3100:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":244,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3070:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":246,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3070:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":255,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3070:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":256,"nodeType":"ExpressionStatement","src":"3070:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":260,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3149:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":263,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3172:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3172:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":262,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3164:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":261,"name":"address","nodeType":"ElementaryTypeName","src":"3164:7:0","typeDescriptions":{}}},"id":265,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3164:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":257,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3137:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":259,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3137:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":266,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3137:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":267,"nodeType":"ExpressionStatement","src":"3137:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":271,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3206:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":276,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3242:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3242:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":275,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3234:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":274,"name":"address","nodeType":"ElementaryTypeName","src":"3234:7:0","typeDescriptions":{}}},"id":278,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3234:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":272,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3222:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":273,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3222:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":279,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3222:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":268,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3194:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3194:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":280,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3194:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":281,"nodeType":"ExpressionStatement","src":"3194:61:0"},{"assignments":[283],"declarations":[{"constant":false,"id":283,"mutability":"mutable","name":"json","nameLocation":"3280:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3266:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":282,"name":"string","nodeType":"ElementaryTypeName","src":"3266:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":285,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":284,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3287:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3266:55:0"},{"assignments":[290],"declarations":[{"constant":false,"id":290,"mutability":"mutable","name":"keys","nameLocation":"3347:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3331:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":288,"name":"string","nodeType":"ElementaryTypeName","src":"3331:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":289,"nodeType":"ArrayTypeName","src":"3331:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":296,"initialValue":{"arguments":[{"id":293,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":283,"src":"3371:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":294,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3377:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":291,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3354:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3354:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":295,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3354:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3331:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":300,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3414:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":301,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3422:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":303,"indexExpression":{"hexValue":"30","id":302,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3427:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3422:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":304,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3431:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":306,"indexExpression":{"hexValue":"31","id":305,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3436:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3431:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":297,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3402:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":299,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":191,"src":"3402:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3402:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":308,"nodeType":"ExpressionStatement","src":"3402:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3461:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":309,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3450:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":311,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3450:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":313,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3450:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":314,"nodeType":"ExpressionStatement","src":"3450:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3517:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":321,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3509:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":320,"name":"uint160","nodeType":"ElementaryTypeName","src":"3509:7:0","typeDescriptions":{}}},"id":323,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":319,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3501:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":318,"name":"address","nodeType":"ElementaryTypeName","src":"3501:7:0","typeDescriptions":{}}},"id":324,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3501:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":315,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3487:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":317,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"3487:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":325,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3487:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":326,"nodeType":"ExpressionStatement","src":"3487:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3545:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":327,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3534:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":329,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3534:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":331,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3534:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":332,"nodeType":"ExpressionStatement","src":"3534:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":336,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3582:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":339,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3617:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":340,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3617:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":338,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3609:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":337,"name":"address","nodeType":"ElementaryTypeName","src":"3609:7:0","typeDescriptions":{}}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3609:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":333,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3570:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3570:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":342,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3570:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":343,"nodeType":"ExpressionStatement","src":"3570:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":347,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3651:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":350,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3689:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3681:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":348,"name":"address","nodeType":"ElementaryTypeName","src":"3681:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":344,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3639:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":346,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3639:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":352,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3639:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":353,"nodeType":"ExpressionStatement","src":"3639:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":357,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3716:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":354,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3705:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":356,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3705:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":358,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3705:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":359,"nodeType":"ExpressionStatement","src":"3705:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":360,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3741:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":362,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"3741:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3741:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":364,"nodeType":"ExpressionStatement","src":"3741:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":368,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3776:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":365,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3765:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":367,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3765:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3765:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":370,"nodeType":"ExpressionStatement","src":"3765:33:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":374,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3821:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":371,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3809:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":373,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"3809:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":375,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3809:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":376,"nodeType":"ExpressionStatement","src":"3809:20:0"}]},"documentation":{"id":216,"nodeType":"StructuredDocumentation","src":"2804:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"2896:3:0","parameters":{"id":217,"nodeType":"ParameterList","parameters":[],"src":"2899:2:0"},"returnParameters":{"id":218,"nodeType":"ParameterList","parameters":[],"src":"2909:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":609,"nodeType":"FunctionDefinition","src":"3903:1258:0","nodes":[],"body":{"id":608,"nodeType":"Block","src":"3934:1227:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3956:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":392,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3999:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":391,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3991:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":390,"name":"address","nodeType":"ElementaryTypeName","src":"3991:7:0","typeDescriptions":{}}},"id":393,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3991:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3979:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":389,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3979:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":394,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3979:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":387,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3971:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":386,"name":"uint256","nodeType":"ElementaryTypeName","src":"3971:7:0","typeDescriptions":{}}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3971:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":382,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3944:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3944:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":396,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3944:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":397,"nodeType":"ExpressionStatement","src":"3944:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":401,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4030:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":398,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4018:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":400,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4018:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":402,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4018:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":403,"nodeType":"ExpressionStatement","src":"4018:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":404,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4057:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":406,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"4057:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":407,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4057:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":408,"nodeType":"ExpressionStatement","src":"4057:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4092:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":409,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4081:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":411,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4081:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":413,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4081:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":414,"nodeType":"ExpressionStatement","src":"4081:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":418,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4128:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":415,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4117:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":417,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4117:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4117:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":420,"nodeType":"ExpressionStatement","src":"4117:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":424,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4166:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":421,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4154:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":423,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4154:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":425,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4154:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":426,"nodeType":"ExpressionStatement","src":"4154:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":434,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4231:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":433,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4223:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":432,"name":"uint160","nodeType":"ElementaryTypeName","src":"4223:7:0","typeDescriptions":{}}},"id":435,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4223:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":431,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4215:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":430,"name":"address","nodeType":"ElementaryTypeName","src":"4215:7:0","typeDescriptions":{}}},"id":436,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4215:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":427,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4197:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":429,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4197:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":437,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4197:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":438,"nodeType":"ExpressionStatement","src":"4197:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":442,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4263:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":439,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4252:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":441,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4252:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":443,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4252:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":444,"nodeType":"ExpressionStatement","src":"4252:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":448,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4302:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":445,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4291:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4291:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":449,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4291:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":450,"nodeType":"ExpressionStatement","src":"4291:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":454,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4344:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":451,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4330:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":705,"src":"4330:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":455,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4330:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":456,"nodeType":"ExpressionStatement","src":"4330:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":457,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4371:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":459,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4371:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":460,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4371:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":461,"nodeType":"ExpressionStatement","src":"4371:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":465,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4410:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":462,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4399:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":464,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4399:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":466,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4399:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":467,"nodeType":"ExpressionStatement","src":"4399:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":471,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4451:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":468,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4439:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":470,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4439:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4439:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":473,"nodeType":"ExpressionStatement","src":"4439:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":481,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4512:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":480,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4504:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":479,"name":"uint160","nodeType":"ElementaryTypeName","src":"4504:7:0","typeDescriptions":{}}},"id":482,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4504:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":478,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4496:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":477,"name":"address","nodeType":"ElementaryTypeName","src":"4496:7:0","typeDescriptions":{}}},"id":483,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4496:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":474,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4478:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":476,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4478:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4478:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":485,"nodeType":"ExpressionStatement","src":"4478:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":489,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4544:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":486,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4531:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":488,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":678,"src":"4531:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":490,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4531:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":491,"nodeType":"ExpressionStatement","src":"4531:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":492,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4563:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":494,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4563:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4563:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":496,"nodeType":"ExpressionStatement","src":"4563:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":500,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4604:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":497,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4592:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":499,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4592:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4592:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":502,"nodeType":"ExpressionStatement","src":"4592:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":510,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4665:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4657:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":508,"name":"uint160","nodeType":"ElementaryTypeName","src":"4657:7:0","typeDescriptions":{}}},"id":511,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":507,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4649:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":506,"name":"address","nodeType":"ElementaryTypeName","src":"4649:7:0","typeDescriptions":{}}},"id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4649:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":503,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4636:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":505,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4636:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":513,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4636:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":514,"nodeType":"ExpressionStatement","src":"4636:40:0"},{"assignments":[517],"declarations":[{"constant":false,"id":517,"mutability":"mutable","name":"x","nameLocation":"4693:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4686:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":516,"nodeType":"UserDefinedTypeName","pathNode":{"id":515,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4686:6:0"},"referencedDeclaration":719,"src":"4686:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":523,"initialValue":{"arguments":[{"hexValue":"31323334","id":521,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4708:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":520,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4697:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":519,"nodeType":"UserDefinedTypeName","pathNode":{"id":518,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4701:6:0"},"referencedDeclaration":719,"src":"4701:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":522,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4697:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4686:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":529,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":525,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":517,"src":"4731:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":526,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4731:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4731:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":528,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4742:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4731:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":524,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4723:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":530,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4723:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":531,"nodeType":"ExpressionStatement","src":"4723:24:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":535,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4770:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":532,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4758:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":534,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4758:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":536,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4758:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":537,"nodeType":"ExpressionStatement","src":"4758:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":545,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4820:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":544,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4812:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":543,"name":"uint160","nodeType":"ElementaryTypeName","src":"4812:7:0","typeDescriptions":{}}},"id":546,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4812:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":542,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4804:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":541,"name":"address","nodeType":"ElementaryTypeName","src":"4804:7:0","typeDescriptions":{}}},"id":547,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4804:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":538,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4791:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":540,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4791:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4791:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":549,"nodeType":"ExpressionStatement","src":"4791:38:0"},{"assignments":[552],"declarations":[{"constant":false,"id":552,"mutability":"mutable","name":"y","nameLocation":"4846:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4839:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":551,"nodeType":"UserDefinedTypeName","pathNode":{"id":550,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4839:6:0"},"referencedDeclaration":719,"src":"4839:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":566,"initialValue":{"arguments":[{"hexValue":"31323334","id":564,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4889:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":555,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4850:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":554,"nodeType":"UserDefinedTypeName","pathNode":{"id":553,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4854:6:0"},"referencedDeclaration":719,"src":"4854:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":563,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4883:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":559,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4875:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":558,"name":"uint256","nodeType":"ElementaryTypeName","src":"4875:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4875:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":557,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4867:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":556,"name":"bytes32","nodeType":"ElementaryTypeName","src":"4867:7:0","typeDescriptions":{}}},"id":562,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4867:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"4850:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":565,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4850:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4839:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":568,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":552,"src":"4912:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":569,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4912:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":570,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4912:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":571,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4923:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4912:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":567,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4904:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4904:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"4904:24:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4950:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4938:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4938:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4938:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"4938:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5042:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5042:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":584,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5042:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":585,"nodeType":"ExpressionStatement","src":"5042:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5077:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":588,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5066:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":587,"nodeType":"UserDefinedTypeName","pathNode":{"id":586,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"5070:6:0"},"referencedDeclaration":719,"src":"5070:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":590,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5066:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":591,"nodeType":"ExpressionStatement","src":"5066:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":595,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5105:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":602,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5146:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":601,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5138:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":600,"name":"address","nodeType":"ElementaryTypeName","src":"5138:7:0","typeDescriptions":{}}},"id":603,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5138:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":598,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5126:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":599,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5126:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5126:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":597,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5118:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":596,"name":"uint256","nodeType":"ElementaryTypeName","src":"5118:7:0","typeDescriptions":{}}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5118:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":592,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5093:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":594,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"5093:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":606,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5093:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":607,"nodeType":"ExpressionStatement","src":"5093:61:0"}]},"documentation":{"id":379,"nodeType":"StructuredDocumentation","src":"3842:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"3912:12:0","parameters":{"id":380,"nodeType":"ParameterList","parameters":[],"src":"3924:2:0"},"returnParameters":{"id":381,"nodeType":"ParameterList","parameters":[],"src":"3934:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":633,"nodeType":"FunctionDefinition","src":"5256:143:0","nodes":[],"body":{"id":632,"nodeType":"Block","src":"5305:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":618,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":612,"src":"5327:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":615,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5315:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":617,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5315:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5315:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":620,"nodeType":"ExpressionStatement","src":"5315:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5352:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":627,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"5380:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":628,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"5380:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":626,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5372:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":625,"name":"address","nodeType":"ElementaryTypeName","src":"5372:7:0","typeDescriptions":{}}},"id":629,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5372:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":621,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5340:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":623,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"5340:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":630,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5340:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":631,"nodeType":"ExpressionStatement","src":"5340:52:0"}]},"documentation":{"id":610,"nodeType":"StructuredDocumentation","src":"5167:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"5265:5:0","parameters":{"id":613,"nodeType":"ParameterList","parameters":[{"constant":false,"id":612,"mutability":"mutable","name":"_v","nameLocation":"5287:2:0","nodeType":"VariableDeclaration","scope":633,"src":"5271:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":611,"name":"string","nodeType":"ElementaryTypeName","src":"5271:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5270:20:0"},"returnParameters":{"id":614,"nodeType":"ParameterList","parameters":[],"src":"5305:0:0"},"scope":706,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":648,"nodeType":"FunctionDefinition","src":"5405:95:0","nodes":[],"body":{"id":647,"nodeType":"Block","src":"5449:51:0","nodes":[],"statements":[{"expression":{"id":639,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5459:9:0","subExpression":{"id":638,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5459:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":640,"nodeType":"ExpressionStatement","src":"5459:9:0"},{"expression":{"arguments":[{"id":644,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":635,"src":"5490:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":641,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5478:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":643,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5478:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":645,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5478:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":646,"nodeType":"ExpressionStatement","src":"5478:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"5414:5:0","parameters":{"id":636,"nodeType":"ParameterList","parameters":[{"constant":false,"id":635,"mutability":"mutable","name":"_v","nameLocation":"5436:2:0","nodeType":"VariableDeclaration","scope":648,"src":"5420:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":634,"name":"string","nodeType":"ElementaryTypeName","src":"5420:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5419:20:0"},"returnParameters":{"id":637,"nodeType":"ParameterList","parameters":[],"src":"5449:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":663,"nodeType":"FunctionDefinition","src":"5506:95:0","nodes":[],"body":{"id":662,"nodeType":"Block","src":"5550:51:0","nodes":[],"statements":[{"expression":{"id":654,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5560:9:0","subExpression":{"id":653,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5560:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":655,"nodeType":"ExpressionStatement","src":"5560:9:0"},{"expression":{"arguments":[{"id":659,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":650,"src":"5591:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":656,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5579:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":658,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5579:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":660,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5579:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":661,"nodeType":"ExpressionStatement","src":"5579:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"5515:5:0","parameters":{"id":651,"nodeType":"ParameterList","parameters":[{"constant":false,"id":650,"mutability":"mutable","name":"_v","nameLocation":"5537:2:0","nodeType":"VariableDeclaration","scope":663,"src":"5521:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":649,"name":"string","nodeType":"ElementaryTypeName","src":"5521:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5520:20:0"},"returnParameters":{"id":652,"nodeType":"ParameterList","parameters":[],"src":"5550:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":678,"nodeType":"FunctionDefinition","src":"5607:98:0","nodes":[],"body":{"id":677,"nodeType":"Block","src":"5653:52:0","nodes":[],"statements":[{"expression":{"id":669,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5663:9:0","subExpression":{"id":668,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5663:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":670,"nodeType":"ExpressionStatement","src":"5663:9:0"},{"expression":{"arguments":[{"id":674,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":665,"src":"5695:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":671,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5682:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":673,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":693,"src":"5682:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":675,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5682:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":676,"nodeType":"ExpressionStatement","src":"5682:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"5616:7:0","parameters":{"id":666,"nodeType":"ParameterList","parameters":[{"constant":false,"id":665,"mutability":"mutable","name":"_v","nameLocation":"5640:2:0","nodeType":"VariableDeclaration","scope":678,"src":"5624:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":664,"name":"string","nodeType":"ElementaryTypeName","src":"5624:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5623:20:0"},"returnParameters":{"id":667,"nodeType":"ParameterList","parameters":[],"src":"5653:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":693,"nodeType":"FunctionDefinition","src":"5711:97:0","nodes":[],"body":{"id":692,"nodeType":"Block","src":"5757:51:0","nodes":[],"statements":[{"expression":{"id":684,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5767:9:0","subExpression":{"id":683,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5767:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":685,"nodeType":"ExpressionStatement","src":"5767:9:0"},{"expression":{"arguments":[{"id":689,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":680,"src":"5798:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":686,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5786:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":688,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5786:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":690,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5786:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":691,"nodeType":"ExpressionStatement","src":"5786:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"5720:7:0","parameters":{"id":681,"nodeType":"ParameterList","parameters":[{"constant":false,"id":680,"mutability":"mutable","name":"_v","nameLocation":"5744:2:0","nodeType":"VariableDeclaration","scope":693,"src":"5728:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":679,"name":"string","nodeType":"ElementaryTypeName","src":"5728:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5727:20:0"},"returnParameters":{"id":682,"nodeType":"ParameterList","parameters":[],"src":"5757:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":705,"nodeType":"FunctionDefinition","src":"5814:84:0","nodes":[],"body":{"id":704,"nodeType":"Block","src":"5866:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":701,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":695,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":698,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5876:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":700,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5876:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":702,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5876:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":703,"nodeType":"ExpressionStatement","src":"5876:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"5823:8:0","parameters":{"id":696,"nodeType":"ParameterList","parameters":[{"constant":false,"id":695,"mutability":"mutable","name":"_v","nameLocation":"5848:2:0","nodeType":"VariableDeclaration","scope":705,"src":"5832:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":694,"name":"string","nodeType":"ElementaryTypeName","src":"5832:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5831:20:0"},"returnParameters":{"id":697,"nodeType":"ParameterList","parameters":[],"src":"5866:0:0"},"scope":706,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[719],"contractKind":"contract","documentation":{"id":193,"nodeType":"StructuredDocumentation","src":"2415:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[706],"name":"ScriptExample","nameLocation":"2550:13:0","scope":720,"usedErrors":[]},{"id":719,"nodeType":"ContractDefinition","src":"5902:96:0","nodes":[{"id":708,"nodeType":"VariableDeclaration","src":"5924:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"5939:3:0","scope":719,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":707,"name":"uint256","nodeType":"ElementaryTypeName","src":"5924:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":718,"nodeType":"FunctionDefinition","src":"5949:47:0","nodes":[],"body":{"id":717,"nodeType":"Block","src":"5972:24:0","nodes":[],"statements":[{"expression":{"id":715,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":713,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":708,"src":"5982:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":714,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":710,"src":"5988:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"5982:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":716,"nodeType":"ExpressionStatement","src":"5982:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":711,"nodeType":"ParameterList","parameters":[{"constant":false,"id":710,"mutability":"mutable","name":"v","nameLocation":"5969:1:0","nodeType":"VariableDeclaration","scope":718,"src":"5961:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":709,"name":"uint256","nodeType":"ElementaryTypeName","src":"5961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"5960:11:0"},"returnParameters":{"id":712,"nodeType":"ParameterList","parameters":[],"src":"5972:0:0"},"scope":719,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[719],"name":"FooBar","nameLocation":"5911:6:0","scope":720,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
+{"abi":[{"type":"function","name":"call1","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"call2","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"callPure","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"pure"},{"type":"function","name":"counter","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"hello","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"nested1","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"nested2","inputs":[{"name":"_v","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"run","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"runBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b506124b2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c8063a76ccdfa11610076578063bef03abc1161005b578063bef03abc14610111578063c040622614610119578063dbf1282f146100c357600080fd5b8063a76ccdfa146100eb578063a777d0dc146100fe57600080fd5b806361bc221a146100a85780637e79255d146100c35780637f8b915c146100d85780638d3ef7ca146100c3575b600080fd5b6100b160005481565b60405190815260200160405180910390f35b6100d66100d1366004611e15565b610121565b005b6100d66100e6366004611e15565b610178565b6100d66100f9366004611e15565b6101b7565b6100d661010c366004611e15565b61023f565b6100d66102bd565b6100d661105f565b60008054908061013083611e87565b919050555061017482828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b5050565b61017482828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b6000805490806101c683611e87565b90915550506040517fdbf1282f000000000000000000000000000000000000000000000000000000008152309063dbf1282f906102099085908590600401611ee6565b600060405180830381600087803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b505050505050565b61027e82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b6101746040518060400160405280601081526020017f68656c6c6f206d73672e73656e6465720000000000000000000000000000000081525033611b99565b604080518082018252600b81527f6e6f6e6365207374617274000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526103909190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab906024015b602060405180830381865afa15801561035d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103819190611f33565b67ffffffffffffffff16611c2a565b6103ce6040518060400160405280600e81526020017f74657374696e672073696e676c65000000000000000000000000000000000000815250611b07565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561043957600080fd5b505af115801561044d573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3100000000000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3200000000000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561056757600080fd5b505af115801561057b573d6000803e3d6000fd5b505050506105bd6040518060400160405280601281526020017f74657374696e672073746172742f73746f700000000000000000000000000000815250611b07565b6040517f7fec2a8d00000000000000000000000000000000000000000000000000000000815262c0ffee6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b15801561062557600080fd5b505af1158015610639573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3100000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3200000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561075357600080fd5b505af1158015610767573d6000803e3d6000fd5b50506040517f7f8b915c00000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f737461727473746f705f707572650000000000000000000000000000000000006044820152309250637f8b915c915060640160006040518083038186803b1580156107e857600080fd5b505afa1580156107fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561086b57600080fd5b505af115801561087f573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3300000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b15801561090257600080fd5b505af1158015610916573d6000803e3d6000fd5b505050506109586040518060400160405280600e81526020017f74657374696e67206e6573746564000000000000000000000000000000000000815250611b07565b6040517f7fec2a8d0000000000000000000000000000000000000000000000000000000081526112346004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b1580156109bf57600080fd5b505af11580156109d3573d6000803e3d6000fd5b50506040517fa76ccdfa00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f6e65737465640000000000000000000000000000000000000000000000000000604482015230925063a76ccdfa9150606401600060405180830381600087803b158015610a5657600080fd5b505af1158015610a6a573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ad957600080fd5b505af1158015610aed573d6000803e3d6000fd5b50505050610b2f6040518060400160405280601381526020017f636f6e7472616374206465706c6f796d656e7400000000000000000000000000815250611b07565b6040517fe6962cdb000000000000000000000000000000000000000000000000000000008152621234566004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b5050505060006104d2604051610bc090611e09565b908152602001604051809103906000f080158015610be2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611f64565b6104d214610ce9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323360448201527f340000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610d276040518060400160405280600881526020017f6372656174652032000000000000000000000000000000000000000000000000815250611b07565b6040517fe6962cdb00000000000000000000000000000000000000000000000000000000815261cafe6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610d8e57600080fd5b505af1158015610da2573d6000803e3d6000fd5b505050506000602a60001b6104d2604051610dbc90611e09565b9081526020018190604051809103906000f5905080158015610de2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e549190611f64565b6104d214610ee4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f466f6f4261723a20666f6f20696e2063726561746532206973206e6f7420313260448201527f33340000000000000000000000000000000000000000000000000000000000006064820152608401610ce0565b610f226040518060400160405280600581526020017f646f6e6521000000000000000000000000000000000000000000000000000000815250611b07565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f8d57600080fd5b505af1158015610fa1573d6000803e3d6000fd5b505050506104d2604051610fb490611e09565b908152602001604051809103906000f080158015610fd6573d6000803e3d6000fd5b5050604080518082018252600981527f6e6f6e636520656e640000000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526101749190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b604080517f4777f3cf0000000000000000000000000000000000000000000000000000000081526004810191909152600c60448201527f4558414d504c455f424f4f4c0000000000000000000000000000000000000000606482015260006024820181905290737109709ecfa91a80626ff3989d68f67f5b1dd12d90634777f3cf90608401602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111259190611f7d565b90506111666040518060400160405280601381526020017f626f6f6c2076616c75652066726f6d20656e760000000000000000000000000081525082611cbb565b6111a56040518060400160405280600d81526020017f636f6e747261637420616464720000000000000000000000000000000000000081525030611b99565b604080518082018252600e81527f636f6e7472616374206e6f6e6365000000000000000000000000000000000000602082015290517f2d0335ab00000000000000000000000000000000000000000000000000000000815230600482015261122c9190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b61126b6040518060400160405280600b81526020017f73656e646572206164647200000000000000000000000000000000000000000081525033611b99565b604080518082018252600c81527f73656e646572206e6f6e63650000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523360048201526112f29190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b60408051808201825260208082527f7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d9082015290517f213e4198000000000000000000000000000000000000000000000000000000008152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063213e419890611377908590600401612019565b600060405180830381865afa158015611394573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113da9190810190612156565b90506114506040518060400160405280600481526020017f6b65797300000000000000000000000000000000000000000000000000000000815250826000815181106114285761142861222e565b6020026020010151836001815181106114435761144361222e565b6020026020010151611d4c565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f66726f6d206f726967696e616c000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156114cd57600080fd5b505afa1580156114e1573d6000803e3d6000fd5b50506040517f06447d5600000000000000000000000000000000000000000000000000000000815260426004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d92506306447d569150602401600060405180830381600087803b15801561154b57600080fd5b505af115801561155f573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b20310000000000000000000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b1580156115e057600080fd5b505afa1580156115f4573d6000803e3d6000fd5b505050506116376040518060400160405280601781526020017f706172656e742073636f7065206d73672e73656e64657200000000000000000081525033611b99565b6116766040518060400160405280601a81526020017f706172656e742073636f706520636f6e74726163742e6164647200000000000081525030611b99565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b203200000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156116f357600080fd5b505afa158015611707573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166390c5013b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561177657600080fd5b505af115801561178a573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f66726f6d206f726967696e616c20616761696e00000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b15801561180b57600080fd5b505afa15801561181f573d6000803e3d6000fd5b50506040517f3ebf73b400000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5363726970744578616d706c652e732e736f6c3a4e6f6e63654765747465720060448201527f12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a2529250737109709ecfa91a80626ff3989d68f67f5b1dd12d915063b4d6c7829083908390633ebf73b490606401600060405180830381865afa1580156118e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261192b919081019061225d565b6040518363ffffffff1660e01b81526004016119489291906122ae565b600060405180830381600087803b15801561196257600080fd5b505af1158015611976573d6000803e3d6000fd5b50506040517fea06029100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d925063ea0602919150602401600060405180830381600087803b1580156119f557600080fd5b505af1158015611a09573d6000803e3d6000fd5b50506040517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526000925073ffffffffffffffffffffffffffffffffffffffff84169150632d0335ab90602401602060405180830381865afa158015611a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9e9190611f64565b9050611ac26040518060800160405280604281526020016124646042913982611c2a565b611b006040518060400160405280600581526020017f646f6e6521000000000000000000000000000000000000000000000000000000815250611b07565b5050505050565b611b9681604051602401611b1b91906122dd565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f41304fac00000000000000000000000000000000000000000000000000000000179052611de4565b50565b6101748282604051602401611baf9291906122f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f319af33300000000000000000000000000000000000000000000000000000000179052611de4565b6101748282604051602401611c40929190612328565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb60e72cc00000000000000000000000000000000000000000000000000000000179052611de4565b6101748282604051602401611cd192919061234a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc3b5563500000000000000000000000000000000000000000000000000000000179052611de4565b611ddf838383604051602401611d649392919061236e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2ced7cef00000000000000000000000000000000000000000000000000000000179052611de4565b505050565b611b968180516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b60b2806123b283390190565b60008060208385031215611e2857600080fd5b823567ffffffffffffffff80821115611e4057600080fd5b818501915085601f830112611e5457600080fd5b813581811115611e6357600080fd5b866020828501011115611e7557600080fd5b60209290920196919550909350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611edf577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600060208284031215611f4557600080fd5b815167ffffffffffffffff81168114611f5d57600080fd5b9392505050565b600060208284031215611f7657600080fd5b5051919050565b600060208284031215611f8f57600080fd5b81518015158114611f5d57600080fd5b60005b83811015611fba578181015183820152602001611fa2565b83811115611fc9576000848401525b50505050565b60008151808452611fe7816020860160208601611f9f565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60408152600061202c6040830184611fcf565b8281036020840152600c81527f2e726f6f745f6b65795b305d000000000000000000000000000000000000000060208201526040810191505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156120e1576120e161206b565b604052919050565b600067ffffffffffffffff8311156121035761210361206b565b61213460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8601160161209a565b905082815283838301111561214857600080fd5b611f5d836020830184611f9f565b6000602080838503121561216957600080fd5b825167ffffffffffffffff8082111561218157600080fd5b818501915085601f83011261219557600080fd5b8151818111156121a7576121a761206b565b8060051b6121b685820161209a565b91825283810185019185810190898411156121d057600080fd5b86860192505b83831015612221578251858111156121ee5760008081fd5b8601603f81018b136122005760008081fd5b6122118b89830151604084016120e9565b83525091860191908601906121d6565b9998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561226f57600080fd5b815167ffffffffffffffff81111561228657600080fd5b8201601f8101841361229757600080fd5b6122a6848251602084016120e9565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006122a66040830184611fcf565b602081526000611f5d6020830184611fcf565b6040815260006123036040830185611fcf565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b60408152600061233b6040830185611fcf565b90508260208301529392505050565b60408152600061235d6040830185611fcf565b905082151560208301529392505050565b6060815260006123816060830186611fcf565b82810360208401526123938186611fcf565b905082810360408401526123a78185611fcf565b969550505050505056fe608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683aa164736f6c634300080f000a","sourceMap":"3123:3912:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b50600436106100a35760003560e01c8063a76ccdfa11610076578063bef03abc1161005b578063bef03abc14610111578063c040622614610119578063dbf1282f146100c357600080fd5b8063a76ccdfa146100eb578063a777d0dc146100fe57600080fd5b806361bc221a146100a85780637e79255d146100c35780637f8b915c146100d85780638d3ef7ca146100c3575b600080fd5b6100b160005481565b60405190815260200160405180910390f35b6100d66100d1366004611e15565b610121565b005b6100d66100e6366004611e15565b610178565b6100d66100f9366004611e15565b6101b7565b6100d661010c366004611e15565b61023f565b6100d66102bd565b6100d661105f565b60008054908061013083611e87565b919050555061017482828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b5050565b61017482828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b6000805490806101c683611e87565b90915550506040517fdbf1282f000000000000000000000000000000000000000000000000000000008152309063dbf1282f906102099085908590600401611ee6565b600060405180830381600087803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b505050505050565b61027e82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b0792505050565b6101746040518060400160405280601081526020017f68656c6c6f206d73672e73656e6465720000000000000000000000000000000081525033611b99565b604080518082018252600b81527f6e6f6e6365207374617274000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526103909190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab906024015b602060405180830381865afa15801561035d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103819190611f33565b67ffffffffffffffff16611c2a565b6103ce6040518060400160405280600e81526020017f74657374696e672073696e676c65000000000000000000000000000000000000815250611b07565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561043957600080fd5b505af115801561044d573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3100000000000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f73696e676c655f63616c6c3200000000000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561056757600080fd5b505af115801561057b573d6000803e3d6000fd5b505050506105bd6040518060400160405280601281526020017f74657374696e672073746172742f73746f700000000000000000000000000000815250611b07565b6040517f7fec2a8d00000000000000000000000000000000000000000000000000000000815262c0ffee6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b15801561062557600080fd5b505af1158015610639573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3100000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50506040517f8d3ef7ca00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3200000000000000000000000000000000006044820152309250638d3ef7ca9150606401600060405180830381600087803b15801561075357600080fd5b505af1158015610767573d6000803e3d6000fd5b50506040517f7f8b915c00000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f737461727473746f705f707572650000000000000000000000000000000000006044820152309250637f8b915c915060640160006040518083038186803b1580156107e857600080fd5b505afa1580156107fc573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561086b57600080fd5b505af115801561087f573d6000803e3d6000fd5b50506040517f7e79255d00000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f737461727473746f705f63616c6c3300000000000000000000000000000000006044820152309250637e79255d9150606401600060405180830381600087803b15801561090257600080fd5b505af1158015610916573d6000803e3d6000fd5b505050506109586040518060400160405280600e81526020017f74657374696e67206e6573746564000000000000000000000000000000000000815250611b07565b6040517f7fec2a8d0000000000000000000000000000000000000000000000000000000081526112346004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d90637fec2a8d90602401600060405180830381600087803b1580156109bf57600080fd5b505af11580156109d3573d6000803e3d6000fd5b50506040517fa76ccdfa00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f6e65737465640000000000000000000000000000000000000000000000000000604482015230925063a76ccdfa9150606401600060405180830381600087803b158015610a5657600080fd5b505af1158015610a6a573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ad957600080fd5b505af1158015610aed573d6000803e3d6000fd5b50505050610b2f6040518060400160405280601381526020017f636f6e7472616374206465706c6f796d656e7400000000000000000000000000815250611b07565b6040517fe6962cdb000000000000000000000000000000000000000000000000000000008152621234566004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b5050505060006104d2604051610bc090611e09565b908152602001604051809103906000f080158015610be2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611f64565b6104d214610ce9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323360448201527f340000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610d276040518060400160405280600881526020017f6372656174652032000000000000000000000000000000000000000000000000815250611b07565b6040517fe6962cdb00000000000000000000000000000000000000000000000000000000815261cafe6004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d9063e6962cdb90602401600060405180830381600087803b158015610d8e57600080fd5b505af1158015610da2573d6000803e3d6000fd5b505050506000602a60001b6104d2604051610dbc90611e09565b9081526020018190604051809103906000f5905080158015610de2573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c29855786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e549190611f64565b6104d214610ee4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f466f6f4261723a20666f6f20696e2063726561746532206973206e6f7420313260448201527f33340000000000000000000000000000000000000000000000000000000000006064820152608401610ce0565b610f226040518060400160405280600581526020017f646f6e6521000000000000000000000000000000000000000000000000000000815250611b07565b7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff1663afc980406040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f8d57600080fd5b505af1158015610fa1573d6000803e3d6000fd5b505050506104d2604051610fb490611e09565b908152602001604051809103906000f080158015610fd6573d6000803e3d6000fd5b5050604080518082018252600981527f6e6f6e636520656e640000000000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526101749190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b604080517f4777f3cf0000000000000000000000000000000000000000000000000000000081526004810191909152600c60448201527f4558414d504c455f424f4f4c0000000000000000000000000000000000000000606482015260006024820181905290737109709ecfa91a80626ff3989d68f67f5b1dd12d90634777f3cf90608401602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111259190611f7d565b90506111666040518060400160405280601381526020017f626f6f6c2076616c75652066726f6d20656e760000000000000000000000000081525082611cbb565b6111a56040518060400160405280600d81526020017f636f6e747261637420616464720000000000000000000000000000000000000081525030611b99565b604080518082018252600e81527f636f6e7472616374206e6f6e6365000000000000000000000000000000000000602082015290517f2d0335ab00000000000000000000000000000000000000000000000000000000815230600482015261122c9190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b61126b6040518060400160405280600b81526020017f73656e646572206164647200000000000000000000000000000000000000000081525033611b99565b604080518082018252600c81527f73656e646572206e6f6e63650000000000000000000000000000000000000000602082015290517f2d0335ab0000000000000000000000000000000000000000000000000000000081523360048201526112f29190737109709ecfa91a80626ff3989d68f67f5b1dd12d90632d0335ab90602401610340565b60408051808201825260208082527f7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d9082015290517f213e4198000000000000000000000000000000000000000000000000000000008152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063213e419890611377908590600401612019565b600060405180830381865afa158015611394573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113da9190810190612156565b90506114506040518060400160405280600481526020017f6b65797300000000000000000000000000000000000000000000000000000000815250826000815181106114285761142861222e565b6020026020010151836001815181106114435761144361222e565b6020026020010151611d4c565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f66726f6d206f726967696e616c000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156114cd57600080fd5b505afa1580156114e1573d6000803e3d6000fd5b50506040517f06447d5600000000000000000000000000000000000000000000000000000000815260426004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d92506306447d569150602401600060405180830381600087803b15801561154b57600080fd5b505af115801561155f573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b20310000000000000000000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b1580156115e057600080fd5b505afa1580156115f4573d6000803e3d6000fd5b505050506116376040518060400160405280601781526020017f706172656e742073636f7065206d73672e73656e64657200000000000000000081525033611b99565b6116766040518060400160405280601a81526020017f706172656e742073636f706520636f6e74726163742e6164647200000000000081525030611b99565b6040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66726f6d207072616e6b203200000000000000000000000000000000000000006044820152309063a777d0dc9060640160006040518083038186803b1580156116f357600080fd5b505afa158015611707573d6000803e3d6000fd5b505050507f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d60001c73ffffffffffffffffffffffffffffffffffffffff166390c5013b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561177657600080fd5b505af115801561178a573d6000803e3d6000fd5b50506040517fa777d0dc00000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f66726f6d206f726967696e616c20616761696e00000000000000000000000000604482015230925063a777d0dc915060640160006040518083038186803b15801561180b57600080fd5b505afa15801561181f573d6000803e3d6000fd5b50506040517f3ebf73b400000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5363726970744578616d706c652e732e736f6c3a4e6f6e63654765747465720060448201527f12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a2529250737109709ecfa91a80626ff3989d68f67f5b1dd12d915063b4d6c7829083908390633ebf73b490606401600060405180830381865afa1580156118e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261192b919081019061225d565b6040518363ffffffff1660e01b81526004016119489291906122ae565b600060405180830381600087803b15801561196257600080fd5b505af1158015611976573d6000803e3d6000fd5b50506040517fea06029100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152737109709ecfa91a80626ff3989d68f67f5b1dd12d925063ea0602919150602401600060405180830381600087803b1580156119f557600080fd5b505af1158015611a09573d6000803e3d6000fd5b50506040517f2d0335ab0000000000000000000000000000000000000000000000000000000081523060048201526000925073ffffffffffffffffffffffffffffffffffffffff84169150632d0335ab90602401602060405180830381865afa158015611a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9e9190611f64565b9050611ac26040518060800160405280604281526020016124646042913982611c2a565b611b006040518060400160405280600581526020017f646f6e6521000000000000000000000000000000000000000000000000000000815250611b07565b5050505050565b611b9681604051602401611b1b91906122dd565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f41304fac00000000000000000000000000000000000000000000000000000000179052611de4565b50565b6101748282604051602401611baf9291906122f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f319af33300000000000000000000000000000000000000000000000000000000179052611de4565b6101748282604051602401611c40929190612328565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb60e72cc00000000000000000000000000000000000000000000000000000000179052611de4565b6101748282604051602401611cd192919061234a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc3b5563500000000000000000000000000000000000000000000000000000000179052611de4565b611ddf838383604051602401611d649392919061236e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2ced7cef00000000000000000000000000000000000000000000000000000000179052611de4565b505050565b611b968180516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b60b2806123b283390190565b60008060208385031215611e2857600080fd5b823567ffffffffffffffff80821115611e4057600080fd5b818501915085601f830112611e5457600080fd5b813581811115611e6357600080fd5b866020828501011115611e7557600080fd5b60209290920196919550909350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611edf577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600060208284031215611f4557600080fd5b815167ffffffffffffffff81168114611f5d57600080fd5b9392505050565b600060208284031215611f7657600080fd5b5051919050565b600060208284031215611f8f57600080fd5b81518015158114611f5d57600080fd5b60005b83811015611fba578181015183820152602001611fa2565b83811115611fc9576000848401525b50505050565b60008151808452611fe7816020860160208601611f9f565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60408152600061202c6040830184611fcf565b8281036020840152600c81527f2e726f6f745f6b65795b305d000000000000000000000000000000000000000060208201526040810191505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156120e1576120e161206b565b604052919050565b600067ffffffffffffffff8311156121035761210361206b565b61213460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8601160161209a565b905082815283838301111561214857600080fd5b611f5d836020830184611f9f565b6000602080838503121561216957600080fd5b825167ffffffffffffffff8082111561218157600080fd5b818501915085601f83011261219557600080fd5b8151818111156121a7576121a761206b565b8060051b6121b685820161209a565b91825283810185019185810190898411156121d057600080fd5b86860192505b83831015612221578251858111156121ee5760008081fd5b8601603f81018b136122005760008081fd5b6122118b89830151604084016120e9565b83525091860191908601906121d6565b9998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561226f57600080fd5b815167ffffffffffffffff81111561228657600080fd5b8201601f8101841361229757600080fd5b6122a6848251602084016120e9565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006122a66040830184611fcf565b602081526000611f5d6020830184611fcf565b6040815260006123036040830185611fcf565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b60408152600061233b6040830185611fcf565b90508260208301529392505050565b60408152600061235d6040830185611fcf565b905082151560208301529392505050565b6060815260006123816060830186611fcf565b82810360208401526123938186611fcf565b905082810360408401526123a78185611fcf565b969550505050505056fe608060405234801561001057600080fd5b506040516100b23803806100b283398101604081905261002f91610037565b600055610050565b60006020828403121561004957600080fd5b5051919050565b60548061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c298557814602d575b600080fd5b603560005481565b60405190815260200160405180910390f3fea164736f6c634300080f000a6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683aa164736f6c634300080f000a","sourceMap":"3123:3912:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3356:22;;;;;;;;;160:25:1;;;148:2;133:18;3356:22:0;;;;;;;6540:95;;;;;;:::i;:::-;;:::i;:::-;;6949:84;;;;;;:::i;:::-;;:::i;6742:98::-;;;;;;:::i;:::-;;:::i;6391:143::-;;;;;;:::i;:::-;;:::i;4963:1333::-;;;:::i;3468:1428::-;;;:::i;6540:95::-;6594:7;:9;;;:7;:9;;;:::i;:::-;;;;;;6613:15;6625:2;;6613:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6613:11:0;;-1:-1:-1;;;6613:15:0:i;:::-;6540:95;;:::o;6949:84::-;7011:15;7023:2;;7011:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7011:11:0;;-1:-1:-1;;;7011:15:0:i;6742:98::-;6798:7;:9;;;:7;:9;;;:::i;:::-;;;;-1:-1:-1;;6817:16:0;;;;;:4;;:12;;:16;;6830:2;;;;6817:16;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6742:98;;:::o;6391:143::-;6450:15;6462:2;;6450:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6450:11:0;;-1:-1:-1;;;6450:15:0:i;:::-;6475:52;;;;;;;;;;;;;;;;;;6515:10;6475:11;:52::i;4963:1333::-;5004:63;;;;;;;;;;;;;;;;5039:26;;;;;5059:4;5039:26;;;1747:74:1;5004:63:0;;;5039:11;;;;1720:18:1;;5039:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5031:35;;5004:11;:63::i;:::-;5078:29;;;;;;;;;;;;;;;;;;:11;:29::i;:::-;3215:28;3207:37;;5117:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5141:26:0;;;;;2327:2:1;5141:26:0;;;2309:21:1;2366:2;2346:18;;;2339:30;2405:14;2385:18;;;2378:42;5141:4:0;;-1:-1:-1;5141:10:0;;-1:-1:-1;2437:18:1;;5141:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5177:26:0;;;;;2668:2:1;5177:26:0;;;2650:21:1;2707:2;2687:18;;;2680:30;2746:14;2726:18;;;2719:42;5177:4:0;;-1:-1:-1;5177:10:0;;-1:-1:-1;2778:18:1;;5177:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5214:33;;;;;;;;;;;;;;;;;;:11;:33::i;:::-;5257:45;;;;;5291:8;5257:45;;;1747:74:1;5257:17:0;;;;1720:18:1;;5257:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5312:29:0;;;;;3009:2:1;5312:29:0;;;2991:21:1;3048:2;3028:18;;;3021:30;3087:17;3067:18;;;3060:45;5312:4:0;;-1:-1:-1;5312:10:0;;-1:-1:-1;3122:18:1;;5312:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5351:29:0;;;;;3353:2:1;5351:29:0;;;3335:21:1;3392:2;3372:18;;;3365:30;3431:17;3411:18;;;3404:45;5351:4:0;;-1:-1:-1;5351:10:0;;-1:-1:-1;3466:18:1;;5351:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5390:31:0;;;;;3697:2:1;5390:31:0;;;3679:21:1;3736:2;3716:18;;;3709:30;3775:16;3755:18;;;3748:44;5390:4:0;;-1:-1:-1;5390:13:0;;-1:-1:-1;3809:18:1;;5390:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3215:28;3207:37;;5431:16;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5459:29:0;;;;;4040:2:1;5459:29:0;;;4022:21:1;4079:2;4059:18;;;4052:30;4118:17;4098:18;;;4091:45;5459:4:0;;-1:-1:-1;5459:10:0;;-1:-1:-1;4153:18:1;;5459:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5499;;;;;;;;;;;;;;;;;;:11;:29::i;:::-;5538:43;;;;;5572:6;5538:43;;;1747:74:1;5538:17:0;;;;1720:18:1;;5538:43:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5591:22:0;;;;;4384:2:1;5591:22:0;;;4366:21:1;4423:1;4403:18;;;4396:29;4461:8;4441:18;;;4434:36;5591:4:0;;-1:-1:-1;5591:12:0;;-1:-1:-1;4487:18:1;;5591:22:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3215:28;3207:37;;5623:16;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5652:34;;;;;;;;;;;;;;;;;;:11;:34::i;:::-;5696:40;;;;;5725:8;5696:40;;;1747:74:1;5696:12:0;;;;1720:18:1;;5696:40:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5746:8;5768:4;5757:16;;;;;:::i;:::-;160:25:1;;;148:2;133:18;5757:16:0;;;;;;;;;;;;;;;;;;;;;;;5746:27;;5791:1;:5;;;:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5802:4;5791:15;5783:61;;;;;;;5100:2:1;5783:61:0;;;5082:21:1;5139:2;5119:18;;;5112:30;5178:34;5158:18;;;5151:62;5249:3;5229:18;;;5222:31;5270:19;;5783:61:0;;;;;;;;;5855:23;;;;;;;;;;;;;;;;;;:11;:23::i;:::-;5888:38;;;;;5917:6;5888:38;;;1747:74:1;5888:12:0;;;;1720:18:1;;5888:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5936:8;5980:2;5964:20;;5986:4;5947:44;;;;;:::i;:::-;160:25:1;;;148:2;133:18;5947:44:0;;;;;;;;;;;;;;;;;;;;;;;;;;;5936:55;;6009:1;:5;;;:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6020:4;6009:15;6001:62;;;;;;;5502:2:1;6001:62:0;;;5484:21:1;5541:2;5521:18;;;5514:30;5580:34;5560:18;;;5553:62;5651:4;5631:18;;;5624:32;5673:19;;6001:62:0;5300:398:1;6001:62:0;6073:20;;;;;;;;;;;;;;;;;;:11;:20::i;:::-;3215:28;3207:37;;6177:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6212:4;6201:16;;;;;:::i;:::-;160:25:1;;;148:2;133:18;6201:16:0;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;6228:61:0;;;;;;;;;;;;;;;;6261:26;;;;;6281:4;6261:26;;;1747:74:1;6228:61:0;;;6261:11;;;;1720:18:1;;6261:26:0;1601:226:1;3468:1428:0;3509:31;;;;;;;;;5909:21:1;;;;5966:2;5946:18;;;5939:30;6005:14;5985:18;;;5978:42;3500:6:0;6072:20:1;;;6065:52;;;3500:6:0;3509:8;;;;6037:19:1;;3509:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3500:40;;3550:37;;;;;;;;;;;;;;;;;;3585:1;3550:11;:37::i;:::-;3598:43;;;;;;;;;;;;;;;;;;3635:4;3598:11;:43::i;:::-;3651:57;;;;;;;;;;;;;;;;3681:26;;;;;3701:4;3681:26;;;1747:74:1;3651:57:0;;;3681:11;;;;1720:18:1;;3681:26:0;1601:226:1;3651:57:0;3718:47;;;;;;;;;;;;;;;;;;3753:10;3718:11;:47::i;:::-;3775:61;;;;;;;;;;;;;;;;3803:32;;;;;3823:10;3803:32;;;1747:74:1;3775:61:0;;;3803:11;;;;1720:18:1;;3803:32:0;1601:226:1;3775:61:0;3847:55;;;;;;;;;;;;;;;;;3935:38;;;;;3847:18;;3935:16;;;;:38;;3847:55;;3935:38;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3912:61;;3983:37;;;;;;;;;;;;;;;;;;4003:4;4008:1;4003:7;;;;;;;;:::i;:::-;;;;;;;4012:4;4017:1;4012:7;;;;;;;;:::i;:::-;;;;;;;3983:11;:37::i;:::-;4031:27;;;;;10225:2:1;4031:27:0;;;10207:21:1;10264:2;10244:18;;;10237:30;10303:15;10283:18;;;10276:43;4031:4:0;;:10;;10336:18:1;;4031:27:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4068:37:0;;;;;4098:4;4068:37;;;1747:74:1;4068:13:0;;-1:-1:-1;4068:13:0;;-1:-1:-1;1720:18:1;;4068:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4115:26:0;;;;;10567:2:1;4115:26:0;;;10549:21:1;10606:2;10586:18;;;10579:30;10645:14;10625:18;;;10618:42;4115:4:0;;-1:-1:-1;4115:10:0;;-1:-1:-1;10677:18:1;;4115:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4151:59;;;;;;;;;;;;;;;;;;4198:10;4151:11;:59::i;:::-;4220:56;;;;;;;;;;;;;;;;;;4270:4;4220:11;:56::i;:::-;4286:26;;;;;10908:2:1;4286:26:0;;;10890:21:1;10947:2;10927:18;;;10920:30;10986:14;10966:18;;;10959:42;4286:4:0;;:10;;11018:18:1;;4286:26:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3215:28;3207:37;;4322:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4346:33:0;;;;;11249:2:1;4346:33:0;;;11231:21:1;11288:2;11268:18;;;11261:30;11327:21;11307:18;;;11300:49;4346:4:0;;-1:-1:-1;4346:10:0;;-1:-1:-1;11366:18:1;;4346:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4593:53:0;;;;;11597:2:1;4593:53:0;;;11579:21:1;11636:2;11616:18;;;11609:30;11675:33;11655:18;;;11648:61;4521:35:0;;-1:-1:-1;4569:7:0;;-1:-1:-1;4569:7:0;;4521:35;;4569:7;;4593:18;;11726::1;;4593:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4569:78;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4657:34:0;;;;;1777:42:1;1765:55;;4657:34:0;;;1747:74:1;4657:18:0;;-1:-1:-1;4657:18:0;;-1:-1:-1;1720:18:1;;4657:34:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4713:51:0;;;;;4758:4;4713:51;;;1747:74:1;4701:9:0;;-1:-1:-1;4713:36:0;;;;-1:-1:-1;4713:36:0;;1720:18:1;;4713:51:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4701:63;;4774:84;;;;;;;;;;;;;;;;;;4856:1;4774:11;:84::i;:::-;4869:20;;;;;;;;;;;;;;;;;;:11;:20::i;:::-;3490:1406;;;;;3468:1428::o;2025:164::-;2080:59;2135:2;2096:42;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2080:15;:59::i;:::-;2025:164;:::o;2577:188::-;2644:71;2707:2;2711;2660:54;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2644:15;:71::i;2383:188::-;2450:71;2513:2;2517;2466:54;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2450:15;:71::i;2195:182::-;2259:68;2319:2;2323;2275:51;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2259:15;:68::i;2771:222::-;2862:81;2931:2;2935;2939;2878:64;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;2862:15;:81::i;:::-;2771:222;;;:::o;1500:133::-;1571:55;1618:7;1737:14;;1209:42;1910:2;1897:16;;1713:21;;1737:14;1897:16;1209:42;1946:5;1935:68;1926:77;;1863:150;;1639:380;:::o;-1:-1:-1:-;;;;;;;;:::o;196:592:1:-;267:6;275;328:2;316:9;307:7;303:23;299:32;296:52;;;344:1;341;334:12;296:52;384:9;371:23;413:18;454:2;446:6;443:14;440:34;;;470:1;467;460:12;440:34;508:6;497:9;493:22;483:32;;553:7;546:4;542:2;538:13;534:27;524:55;;575:1;572;565:12;524:55;615:2;602:16;641:2;633:6;630:14;627:34;;;657:1;654;647:12;627:34;702:7;697:2;688:6;684:2;680:15;676:24;673:37;670:57;;;723:1;720;713:12;670:57;754:2;746:11;;;;;776:6;;-1:-1:-1;196:592:1;;-1:-1:-1;;;;196:592:1:o;793:349::-;832:3;863:66;856:5;853:77;850:257;;963:77;960:1;953:88;1064:4;1061:1;1054:15;1092:4;1089:1;1082:15;850:257;-1:-1:-1;1134:1:1;1123:13;;793:349::o;1147:449::-;1306:2;1295:9;1288:21;1345:6;1340:2;1329:9;1325:18;1318:34;1402:6;1394;1389:2;1378:9;1374:18;1361:48;1458:1;1429:22;;;1453:2;1425:31;;;1418:42;;;;1512:2;1500:15;;;1517:66;1496:88;1481:104;1477:113;;1147:449;-1:-1:-1;1147:449:1:o;1832:288::-;1901:6;1954:2;1942:9;1933:7;1929:23;1925:32;1922:52;;;1970:1;1967;1960:12;1922:52;2002:9;1996:16;2052:18;2045:5;2041:30;2034:5;2031:41;2021:69;;2086:1;2083;2076:12;2021:69;2109:5;1832:288;-1:-1:-1;;;1832:288:1:o;4709:184::-;4779:6;4832:2;4820:9;4811:7;4807:23;4803:32;4800:52;;;4848:1;4845;4838:12;4800:52;-1:-1:-1;4871:16:1;;4709:184;-1:-1:-1;4709:184:1:o;6128:277::-;6195:6;6248:2;6236:9;6227:7;6223:23;6219:32;6216:52;;;6264:1;6261;6254:12;6216:52;6296:9;6290:16;6349:5;6342:13;6335:21;6328:5;6325:32;6315:60;;6371:1;6368;6361:12;6410:258;6482:1;6492:113;6506:6;6503:1;6500:13;6492:113;;;6582:11;;;6576:18;6563:11;;;6556:39;6528:2;6521:10;6492:113;;;6623:6;6620:1;6617:13;6614:48;;;6658:1;6649:6;6644:3;6640:16;6633:27;6614:48;;6410:258;;;:::o;6673:317::-;6715:3;6753:5;6747:12;6780:6;6775:3;6768:19;6796:63;6852:6;6845:4;6840:3;6836:14;6829:4;6822:5;6818:16;6796:63;:::i;:::-;6904:2;6892:15;6909:66;6888:88;6879:98;;;;6979:4;6875:109;;6673:317;-1:-1:-1;;6673:317:1:o;6995:493::-;7245:2;7234:9;7227:21;7208:4;7271:45;7312:2;7301:9;7297:18;7289:6;7271:45;:::i;:::-;7364:9;7356:6;7352:22;7347:2;7336:9;7332:18;7325:50;7399:2;7391:6;7384:18;7435:14;7430:2;7422:6;7418:15;7411:39;7479:2;7471:6;7467:15;7459:23;;;6995:493;;;;:::o;7493:184::-;7545:77;7542:1;7535:88;7642:4;7639:1;7632:15;7666:4;7663:1;7656:15;7682:334;7753:2;7747:9;7809:2;7799:13;;7814:66;7795:86;7783:99;;7912:18;7897:34;;7933:22;;;7894:62;7891:88;;;7959:18;;:::i;:::-;7995:2;7988:22;7682:334;;-1:-1:-1;7682:334:1:o;8021:437::-;8097:5;8131:18;8123:6;8120:30;8117:56;;;8153:18;;:::i;:::-;8191:116;8301:4;8232:66;8227:2;8219:6;8215:15;8211:88;8207:99;8191:116;:::i;:::-;8182:125;;8330:6;8323:5;8316:21;8370:3;8361:6;8356:3;8352:16;8349:25;8346:45;;;8387:1;8384;8377:12;8346:45;8400:52;8445:6;8438:4;8431:5;8427:16;8422:3;8400:52;:::i;8463:1366::-;8568:6;8599:2;8642;8630:9;8621:7;8617:23;8613:32;8610:52;;;8658:1;8655;8648:12;8610:52;8691:9;8685:16;8720:18;8761:2;8753:6;8750:14;8747:34;;;8777:1;8774;8767:12;8747:34;8815:6;8804:9;8800:22;8790:32;;8860:7;8853:4;8849:2;8845:13;8841:27;8831:55;;8882:1;8879;8872:12;8831:55;8911:2;8905:9;8933:2;8929;8926:10;8923:36;;;8939:18;;:::i;:::-;8985:2;8982:1;8978:10;9008:28;9032:2;9028;9024:11;9008:28;:::i;:::-;9070:15;;;9140:11;;;9136:20;;;9101:12;;;;9168:19;;;9165:39;;;9200:1;9197;9190:12;9165:39;9232:2;9228;9224:11;9213:22;;9244:555;9260:6;9255:3;9252:15;9244:555;;;9339:3;9333:10;9375:2;9362:11;9359:19;9356:109;;;9419:1;9448:2;9444;9437:14;9356:109;9488:20;;9543:2;9535:11;;9531:25;-1:-1:-1;9521:123:1;;9598:1;9627:2;9623;9616:14;9521:123;9669:87;9748:7;9742:2;9738;9734:11;9728:18;9723:2;9719;9715:11;9669:87;:::i;:::-;9657:100;;-1:-1:-1;9277:12:1;;;;9777;;;;9244:555;;;9818:5;8463:1366;-1:-1:-1;;;;;;;;;8463:1366:1:o;9834:184::-;9886:77;9883:1;9876:88;9983:4;9980:1;9973:15;10007:4;10004:1;9997:15;11755:458;11834:6;11887:2;11875:9;11866:7;11862:23;11858:32;11855:52;;;11903:1;11900;11893:12;11855:52;11936:9;11930:16;11969:18;11961:6;11958:30;11955:50;;;12001:1;11998;11991:12;11955:50;12024:22;;12077:4;12069:13;;12065:27;-1:-1:-1;12055:55:1;;12106:1;12103;12096:12;12055:55;12129:78;12199:7;12194:2;12188:9;12183:2;12179;12175:11;12129:78;:::i;:::-;12119:88;11755:458;-1:-1:-1;;;;11755:458:1:o;12218:338::-;12405:42;12397:6;12393:55;12382:9;12375:74;12485:2;12480;12469:9;12465:18;12458:30;12356:4;12505:45;12546:2;12535:9;12531:18;12523:6;12505:45;:::i;12561:220::-;12710:2;12699:9;12692:21;12673:4;12730:45;12771:2;12760:9;12756:18;12748:6;12730:45;:::i;12786:340::-;12963:2;12952:9;12945:21;12926:4;12983:45;13024:2;13013:9;13009:18;13001:6;12983:45;:::i;:::-;12975:53;;13076:42;13068:6;13064:55;13059:2;13048:9;13044:18;13037:83;12786:340;;;;;:::o;13131:291::-;13308:2;13297:9;13290:21;13271:4;13328:45;13369:2;13358:9;13354:18;13346:6;13328:45;:::i;:::-;13320:53;;13409:6;13404:2;13393:9;13389:18;13382:34;13131:291;;;;;:::o;13427:301::-;13598:2;13587:9;13580:21;13561:4;13618:45;13659:2;13648:9;13644:18;13636:6;13618:45;:::i;:::-;13610:53;;13713:6;13706:14;13699:22;13694:2;13683:9;13679:18;13672:50;13427:301;;;;;:::o;13733:546::-;13978:2;13967:9;13960:21;13941:4;14004:45;14045:2;14034:9;14030:18;14022:6;14004:45;:::i;:::-;14097:9;14089:6;14085:22;14080:2;14069:9;14065:18;14058:50;14131:33;14157:6;14149;14131:33;:::i;:::-;14117:47;;14212:9;14204:6;14200:22;14195:2;14184:9;14180:18;14173:50;14240:33;14266:6;14258;14240:33;:::i;:::-;14232:41;13733:546;-1:-1:-1;;;;;;13733:546:1:o","linkReferences":{}},"methodIdentifiers":{"call1(string)":"7e79255d","call2(string)":"8d3ef7ca","callPure(string)":"7f8b915c","counter()":"61bc221a","hello(string)":"a777d0dc","nested1(string)":"a76ccdfa","nested2(string)":"dbf1282f","run()":"c0406226","runBroadcast()":"bef03abc"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"call1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"call2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"callPure\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"hello\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"nested1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_v\",\"type\":\"string\"}],\"name\":\"nested2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"run\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"runBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"ScriptExample\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"hello(string)\":{\"notice\":\"example external function, to force a CALL, and test vm.startPrank with.\"},\"run()\":{\"notice\":\"example function, runs through basic cheat-codes and console logs.\"},\"runBroadcast()\":{\"notice\":\"example function, to test vm.broadcast with.\"}},\"notice\":\"ScriptExample is an example script. The Go forge script code tests that it can run this.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"ScriptExample\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"call1"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"call2"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"pure","type":"function","name":"callPure"},{"inputs":[],"stateMutability":"view","type":"function","name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"view","type":"function","name":"hello"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"nested1"},{"inputs":[{"internalType":"string","name":"_v","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"nested2"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"run"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"runBroadcast"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{"hello(string)":{"notice":"example external function, to force a CALL, and test vm.startPrank with."},"run()":{"notice":"example function, runs through basic cheat-codes and console logs."},"runBroadcast()":{"notice":"example function, to test vm.broadcast with."}},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"ScriptExample"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[{"astId":243,"contract":"scripts/ScriptExample.s.sol:ScriptExample","label":"counter","offset":0,"slot":"0","type":"t_uint256"}],"types":{"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}},"userdoc":{"version":1,"kind":"user","methods":{"hello(string)":{"notice":"example external function, to force a CALL, and test vm.startPrank with."},"run()":{"notice":"example function, runs through basic cheat-codes and console logs."},"runBroadcast()":{"notice":"example function, to test vm.broadcast with."}},"notice":"ScriptExample is an example script. The Go forge script code tests that it can run this."},"devdoc":{"version":1,"kind":"dev","title":"ScriptExample"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/Vm.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/Vm.json
index 34175684c5aa..386172712744 100644
--- a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/Vm.json
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/Vm.json
@@ -1 +1 @@
-{"abi":[{"type":"function","name":"broadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"broadcast","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"envOr","inputs":[{"name":"name","type":"string","internalType":"string"},{"name":"defaultValue","type":"bool","internalType":"bool"}],"outputs":[{"name":"value","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"getNonce","inputs":[{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"nonce","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"parseJsonKeys","inputs":[{"name":"json","type":"string","internalType":"string"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"keys","type":"string[]","internalType":"string[]"}],"stateMutability":"pure"},{"type":"function","name":"startBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"startBroadcast","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"startPrank","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stopBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stopPrank","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"broadcast()":"afc98040","broadcast(address)":"e6962cdb","envOr(string,bool)":"4777f3cf","getNonce(address)":"2d0335ab","parseJsonKeys(string,string)":"213e4198","startBroadcast()":"7fb5297f","startBroadcast(address)":"7fec2a8d","startPrank(address)":"06447d56","stopBroadcast()":"76eadd36","stopPrank()":"90c5013b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"broadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"broadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"defaultValue\",\"type\":\"bool\"}],\"name\":\"envOr\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"value\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"json\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"}],\"name\":\"parseJsonKeys\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"keys\",\"type\":\"string[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"startBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"startBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"startPrank\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stopBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stopPrank\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"Vm\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3\",\"dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"broadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"broadcast"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"bool","name":"defaultValue","type":"bool"}],"stateMutability":"view","type":"function","name":"envOr","outputs":[{"internalType":"bool","name":"value","type":"bool"}]},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"getNonce","outputs":[{"internalType":"uint64","name":"nonce","type":"uint64"}]},{"inputs":[{"internalType":"string","name":"json","type":"string"},{"internalType":"string","name":"key","type":"string"}],"stateMutability":"pure","type":"function","name":"parseJsonKeys","outputs":[{"internalType":"string[]","name":"keys","type":"string[]"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"startBroadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"startBroadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"startPrank"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"stopBroadcast"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"stopPrank"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"Vm"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d","urls":["bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3","dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":720,"exportedSymbols":{"FooBar":[719],"ScriptExample":[706],"Vm":[55],"console":[192]},"nodeType":"SourceUnit","src":"32:5967:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":55,"nodeType":"ContractDefinition","src":"120:616:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":55,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[55],"name":"Vm","nameLocation":"130:2:0","scope":720,"usedErrors":[]},{"id":192,"nodeType":"ContractDefinition","src":"791:1622:0","nodes":[{"id":61,"nodeType":"VariableDeclaration","src":"813:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"830:15:0","scope":192,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":56,"name":"address","nodeType":"ElementaryTypeName","src":"813:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":59,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"856:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":58,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"848:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":57,"name":"address","nodeType":"ElementaryTypeName","src":"848:7:0","typeDescriptions":{}}},"id":60,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"848:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":78,"nodeType":"FunctionDefinition","src":"906:221:0","nodes":[],"body":{"id":77,"nodeType":"Block","src":"1065:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1084:37:0","statements":[{"nodeType":"YulAssignment","src":"1098:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1107:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1098:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":67,"isOffset":false,"isSlot":false,"src":"1107:4:0","valueSize":1},{"declaration":74,"isOffset":false,"isSlot":false,"src":"1098:5:0","valueSize":1}],"id":76,"nodeType":"InlineAssembly","src":"1075:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"915:25:0","parameters":{"id":68,"nodeType":"ParameterList","parameters":[{"constant":false,"id":67,"mutability":"mutable","name":"fnIn","nameLocation":"987:4:0","nodeType":"VariableDeclaration","scope":78,"src":"950:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":66,"nodeType":"FunctionTypeName","parameterTypes":{"id":64,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":66,"src":"959:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":62,"name":"bytes","nodeType":"ElementaryTypeName","src":"959:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"958:14:0"},"returnParameterTypes":{"id":65,"nodeType":"ParameterList","parameters":[],"src":"987:0:0"},"src":"950:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"940:57:0"},"returnParameters":{"id":75,"nodeType":"ParameterList","parameters":[{"constant":false,"id":74,"mutability":"mutable","name":"fnOut","nameLocation":"1058:5:0","nodeType":"VariableDeclaration","scope":78,"src":"1021:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":73,"nodeType":"FunctionTypeName","parameterTypes":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":73,"src":"1030:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":69,"name":"bytes","nodeType":"ElementaryTypeName","src":"1030:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1029:14:0"},"returnParameterTypes":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"1058:0:0"},"src":"1021:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1020:44:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":90,"nodeType":"FunctionDefinition","src":"1133:133:0","nodes":[],"body":{"id":89,"nodeType":"Block","src":"1194:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":86,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":80,"src":"1251:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":84,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1230:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":83,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":78,"src":"1204:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":85,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":87,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":88,"nodeType":"ExpressionStatement","src":"1204:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1142:15:0","parameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"payload","nameLocation":"1171:7:0","nodeType":"VariableDeclaration","scope":90,"src":"1158:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":79,"name":"bytes","nodeType":"ElementaryTypeName","src":"1158:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1157:22:0"},"returnParameters":{"id":82,"nodeType":"ParameterList","parameters":[],"src":"1194:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1272:380:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1336:316:0","nodes":[],"statements":[{"assignments":[96],"declarations":[{"constant":false,"id":96,"mutability":"mutable","name":"payloadLength","nameLocation":"1354:13:0","nodeType":"VariableDeclaration","scope":105,"src":"1346:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":95,"name":"uint256","nodeType":"ElementaryTypeName","src":"1346:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":99,"initialValue":{"expression":{"id":97,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":92,"src":"1370:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":98,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1370:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1346:38:0"},{"assignments":[101],"declarations":[{"constant":false,"id":101,"mutability":"mutable","name":"consoleAddress","nameLocation":"1402:14:0","nodeType":"VariableDeclaration","scope":105,"src":"1394:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":100,"name":"address","nodeType":"ElementaryTypeName","src":"1394:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":103,"initialValue":{"id":102,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":61,"src":"1419:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1394:40:0"},{"AST":{"nodeType":"YulBlock","src":"1496:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1510:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1534:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1543:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1530:3:0"},"nodeType":"YulFunctionCall","src":"1530:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1514:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1559:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1579:3:0"},"nodeType":"YulFunctionCall","src":"1579:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1586:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1602:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1616:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1631:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1634:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1568:10:0"},"nodeType":"YulFunctionCall","src":"1568:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1563:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":101,"isOffset":false,"isSlot":false,"src":"1586:14:0","valueSize":1},{"declaration":92,"isOffset":false,"isSlot":false,"src":"1534:7:0","valueSize":1},{"declaration":96,"isOffset":false,"isSlot":false,"src":"1616:13:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1487:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1281:19:0","parameters":{"id":93,"nodeType":"ParameterList","parameters":[{"constant":false,"id":92,"mutability":"mutable","name":"payload","nameLocation":"1314:7:0","nodeType":"VariableDeclaration","scope":106,"src":"1301:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":91,"name":"bytes","nodeType":"ElementaryTypeName","src":"1301:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1300:22:0"},"returnParameters":{"id":94,"nodeType":"ParameterList","parameters":[],"src":"1336:0:0"},"scope":192,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":120,"nodeType":"FunctionDefinition","src":"1658:121:0","nodes":[],"body":{"id":119,"nodeType":"Block","src":"1703:76:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":114,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1753:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":115,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1768:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":112,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1729:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":113,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1729:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":116,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1729:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":111,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1713:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":117,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1713:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":118,"nodeType":"ExpressionStatement","src":"1713:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1667:3:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"p0","nameLocation":"1685:2:0","nodeType":"VariableDeclaration","scope":120,"src":"1671:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":107,"name":"string","nodeType":"ElementaryTypeName","src":"1671:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1670:18:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":137,"nodeType":"FunctionDefinition","src":"1785:139:0","nodes":[],"body":{"id":136,"nodeType":"Block","src":"1839:85:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":130,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1889:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":131,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":122,"src":"1909:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":132,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":124,"src":"1913:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":128,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1865:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":129,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1865:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":133,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1865:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":127,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1849:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":134,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1849:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":135,"nodeType":"ExpressionStatement","src":"1849:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1794:3:0","parameters":{"id":125,"nodeType":"ParameterList","parameters":[{"constant":false,"id":122,"mutability":"mutable","name":"p0","nameLocation":"1812:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1798:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":121,"name":"string","nodeType":"ElementaryTypeName","src":"1798:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":124,"mutability":"mutable","name":"p1","nameLocation":"1821:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1816:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":123,"name":"bool","nodeType":"ElementaryTypeName","src":"1816:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"1797:27:0"},"returnParameters":{"id":126,"nodeType":"ParameterList","parameters":[],"src":"1839:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":154,"nodeType":"FunctionDefinition","src":"1930:145:0","nodes":[],"body":{"id":153,"nodeType":"Block","src":"1987:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":147,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2037:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":148,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":139,"src":"2060:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":149,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":141,"src":"2064:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":145,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2013:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":146,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2013:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":150,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2013:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":144,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1997:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":151,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1997:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":152,"nodeType":"ExpressionStatement","src":"1997:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1939:3:0","parameters":{"id":142,"nodeType":"ParameterList","parameters":[{"constant":false,"id":139,"mutability":"mutable","name":"p0","nameLocation":"1957:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1943:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":138,"name":"string","nodeType":"ElementaryTypeName","src":"1943:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":141,"mutability":"mutable","name":"p1","nameLocation":"1969:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1961:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":140,"name":"uint256","nodeType":"ElementaryTypeName","src":"1961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1942:30:0"},"returnParameters":{"id":143,"nodeType":"ParameterList","parameters":[],"src":"1987:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":171,"nodeType":"FunctionDefinition","src":"2081:145:0","nodes":[],"body":{"id":170,"nodeType":"Block","src":"2138:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":164,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2188:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":165,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":156,"src":"2211:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":166,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":158,"src":"2215:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":162,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2164:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":163,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2164:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":167,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2164:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":161,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2148:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":168,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2148:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":169,"nodeType":"ExpressionStatement","src":"2148:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2090:3:0","parameters":{"id":159,"nodeType":"ParameterList","parameters":[{"constant":false,"id":156,"mutability":"mutable","name":"p0","nameLocation":"2108:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2094:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":155,"name":"string","nodeType":"ElementaryTypeName","src":"2094:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":158,"mutability":"mutable","name":"p1","nameLocation":"2120:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2112:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":157,"name":"address","nodeType":"ElementaryTypeName","src":"2112:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2093:30:0"},"returnParameters":{"id":160,"nodeType":"ParameterList","parameters":[],"src":"2138:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":191,"nodeType":"FunctionDefinition","src":"2232:179:0","nodes":[],"body":{"id":190,"nodeType":"Block","src":"2313:98:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":183,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2363:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":184,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":173,"src":"2392:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":185,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":175,"src":"2396:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":186,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":177,"src":"2400:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":181,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2339:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":182,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2339:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":187,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2339:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":180,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2323:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":188,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2323:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":189,"nodeType":"ExpressionStatement","src":"2323:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2241:3:0","parameters":{"id":178,"nodeType":"ParameterList","parameters":[{"constant":false,"id":173,"mutability":"mutable","name":"p0","nameLocation":"2259:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2245:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":172,"name":"string","nodeType":"ElementaryTypeName","src":"2245:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":175,"mutability":"mutable","name":"p1","nameLocation":"2277:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2263:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":174,"name":"string","nodeType":"ElementaryTypeName","src":"2263:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":177,"mutability":"mutable","name":"p2","nameLocation":"2295:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2281:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":176,"name":"string","nodeType":"ElementaryTypeName","src":"2281:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2244:54:0"},"returnParameters":{"id":179,"nodeType":"ParameterList","parameters":[],"src":"2313:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[192],"name":"console","nameLocation":"799:7:0","scope":720,"usedErrors":[]},{"id":706,"nodeType":"ContractDefinition","src":"2541:3359:0","nodes":[{"id":207,"nodeType":"VariableDeclaration","src":"2571:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"2597:10:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":194,"name":"address","nodeType":"ElementaryTypeName","src":"2571:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":202,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2644:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":201,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"2634:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":203,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2634:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":200,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2626:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":199,"name":"uint256","nodeType":"ElementaryTypeName","src":"2626:7:0","typeDescriptions":{}}},"id":204,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2626:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":198,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2618:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":197,"name":"uint160","nodeType":"ElementaryTypeName","src":"2618:7:0","typeDescriptions":{}}},"id":205,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2618:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":196,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2610:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":195,"name":"address","nodeType":"ElementaryTypeName","src":"2610:7:0","typeDescriptions":{}}},"id":206,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2610:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":213,"nodeType":"VariableDeclaration","src":"2671:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"2692:2:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"},"typeName":{"id":209,"nodeType":"UserDefinedTypeName","pathNode":{"id":208,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":55,"src":"2671:2:0"},"referencedDeclaration":55,"src":"2671:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"value":{"arguments":[{"id":211,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":207,"src":"2700:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":210,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":55,"src":"2697:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$55_$","typeString":"type(contract Vm)"}},"id":212,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2697:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"visibility":"internal"},{"id":215,"nodeType":"VariableDeclaration","src":"2775:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"2790:7:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":214,"name":"uint256","nodeType":"ElementaryTypeName","src":"2775:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":378,"nodeType":"FunctionDefinition","src":"2887:949:0","nodes":[],"body":{"id":377,"nodeType":"Block","src":"2909:927:0","nodes":[],"statements":[{"assignments":[220],"declarations":[{"constant":false,"id":220,"mutability":"mutable","name":"x","nameLocation":"2924:1:0","nodeType":"VariableDeclaration","scope":377,"src":"2919:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":219,"name":"bool","nodeType":"ElementaryTypeName","src":"2919:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":226,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":223,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2937:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":224,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"2953:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":221,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"2928:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":222,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"2928:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":225,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2928:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"2919:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2981:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":231,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3004:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":227,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"2969:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":229,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":137,"src":"2969:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":232,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2969:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":233,"nodeType":"ExpressionStatement","src":"2969:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":237,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3029:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":240,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3054:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":239,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3046:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":238,"name":"address","nodeType":"ElementaryTypeName","src":"3046:7:0","typeDescriptions":{}}},"id":241,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3046:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":234,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3017:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":236,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3017:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":242,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3017:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":243,"nodeType":"ExpressionStatement","src":"3017:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":247,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3082:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":252,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3120:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":251,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3112:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":250,"name":"address","nodeType":"ElementaryTypeName","src":"3112:7:0","typeDescriptions":{}}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3112:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":248,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3100:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":249,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3100:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":254,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3100:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":244,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3070:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":246,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3070:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":255,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3070:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":256,"nodeType":"ExpressionStatement","src":"3070:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":260,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3149:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":263,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3172:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3172:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":262,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3164:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":261,"name":"address","nodeType":"ElementaryTypeName","src":"3164:7:0","typeDescriptions":{}}},"id":265,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3164:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":257,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3137:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":259,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3137:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":266,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3137:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":267,"nodeType":"ExpressionStatement","src":"3137:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":271,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3206:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":276,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3242:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3242:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":275,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3234:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":274,"name":"address","nodeType":"ElementaryTypeName","src":"3234:7:0","typeDescriptions":{}}},"id":278,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3234:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":272,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3222:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":273,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3222:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":279,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3222:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":268,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3194:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3194:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":280,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3194:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":281,"nodeType":"ExpressionStatement","src":"3194:61:0"},{"assignments":[283],"declarations":[{"constant":false,"id":283,"mutability":"mutable","name":"json","nameLocation":"3280:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3266:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":282,"name":"string","nodeType":"ElementaryTypeName","src":"3266:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":285,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":284,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3287:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3266:55:0"},{"assignments":[290],"declarations":[{"constant":false,"id":290,"mutability":"mutable","name":"keys","nameLocation":"3347:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3331:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":288,"name":"string","nodeType":"ElementaryTypeName","src":"3331:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":289,"nodeType":"ArrayTypeName","src":"3331:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":296,"initialValue":{"arguments":[{"id":293,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":283,"src":"3371:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":294,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3377:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":291,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3354:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3354:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":295,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3354:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3331:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":300,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3414:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":301,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3422:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":303,"indexExpression":{"hexValue":"30","id":302,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3427:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3422:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":304,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3431:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":306,"indexExpression":{"hexValue":"31","id":305,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3436:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3431:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":297,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3402:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":299,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":191,"src":"3402:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3402:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":308,"nodeType":"ExpressionStatement","src":"3402:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3461:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":309,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3450:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":311,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3450:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":313,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3450:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":314,"nodeType":"ExpressionStatement","src":"3450:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3517:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":321,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3509:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":320,"name":"uint160","nodeType":"ElementaryTypeName","src":"3509:7:0","typeDescriptions":{}}},"id":323,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":319,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3501:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":318,"name":"address","nodeType":"ElementaryTypeName","src":"3501:7:0","typeDescriptions":{}}},"id":324,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3501:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":315,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3487:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":317,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"3487:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":325,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3487:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":326,"nodeType":"ExpressionStatement","src":"3487:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3545:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":327,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3534:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":329,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3534:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":331,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3534:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":332,"nodeType":"ExpressionStatement","src":"3534:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":336,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3582:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":339,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3617:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":340,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3617:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":338,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3609:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":337,"name":"address","nodeType":"ElementaryTypeName","src":"3609:7:0","typeDescriptions":{}}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3609:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":333,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3570:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3570:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":342,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3570:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":343,"nodeType":"ExpressionStatement","src":"3570:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":347,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3651:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":350,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3689:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3681:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":348,"name":"address","nodeType":"ElementaryTypeName","src":"3681:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":344,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3639:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":346,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3639:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":352,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3639:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":353,"nodeType":"ExpressionStatement","src":"3639:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":357,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3716:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":354,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3705:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":356,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3705:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":358,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3705:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":359,"nodeType":"ExpressionStatement","src":"3705:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":360,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3741:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":362,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"3741:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3741:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":364,"nodeType":"ExpressionStatement","src":"3741:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":368,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3776:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":365,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3765:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":367,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3765:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3765:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":370,"nodeType":"ExpressionStatement","src":"3765:33:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":374,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3821:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":371,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3809:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":373,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"3809:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":375,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3809:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":376,"nodeType":"ExpressionStatement","src":"3809:20:0"}]},"documentation":{"id":216,"nodeType":"StructuredDocumentation","src":"2804:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"2896:3:0","parameters":{"id":217,"nodeType":"ParameterList","parameters":[],"src":"2899:2:0"},"returnParameters":{"id":218,"nodeType":"ParameterList","parameters":[],"src":"2909:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":609,"nodeType":"FunctionDefinition","src":"3903:1258:0","nodes":[],"body":{"id":608,"nodeType":"Block","src":"3934:1227:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3956:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":392,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3999:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":391,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3991:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":390,"name":"address","nodeType":"ElementaryTypeName","src":"3991:7:0","typeDescriptions":{}}},"id":393,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3991:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3979:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":389,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3979:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":394,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3979:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":387,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3971:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":386,"name":"uint256","nodeType":"ElementaryTypeName","src":"3971:7:0","typeDescriptions":{}}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3971:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":382,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3944:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3944:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":396,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3944:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":397,"nodeType":"ExpressionStatement","src":"3944:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":401,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4030:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":398,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4018:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":400,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4018:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":402,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4018:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":403,"nodeType":"ExpressionStatement","src":"4018:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":404,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4057:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":406,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"4057:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":407,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4057:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":408,"nodeType":"ExpressionStatement","src":"4057:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4092:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":409,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4081:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":411,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4081:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":413,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4081:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":414,"nodeType":"ExpressionStatement","src":"4081:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":418,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4128:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":415,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4117:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":417,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4117:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4117:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":420,"nodeType":"ExpressionStatement","src":"4117:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":424,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4166:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":421,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4154:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":423,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4154:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":425,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4154:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":426,"nodeType":"ExpressionStatement","src":"4154:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":434,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4231:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":433,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4223:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":432,"name":"uint160","nodeType":"ElementaryTypeName","src":"4223:7:0","typeDescriptions":{}}},"id":435,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4223:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":431,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4215:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":430,"name":"address","nodeType":"ElementaryTypeName","src":"4215:7:0","typeDescriptions":{}}},"id":436,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4215:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":427,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4197:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":429,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4197:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":437,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4197:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":438,"nodeType":"ExpressionStatement","src":"4197:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":442,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4263:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":439,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4252:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":441,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4252:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":443,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4252:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":444,"nodeType":"ExpressionStatement","src":"4252:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":448,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4302:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":445,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4291:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4291:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":449,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4291:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":450,"nodeType":"ExpressionStatement","src":"4291:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":454,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4344:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":451,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4330:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":705,"src":"4330:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":455,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4330:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":456,"nodeType":"ExpressionStatement","src":"4330:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":457,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4371:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":459,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4371:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":460,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4371:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":461,"nodeType":"ExpressionStatement","src":"4371:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":465,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4410:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":462,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4399:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":464,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4399:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":466,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4399:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":467,"nodeType":"ExpressionStatement","src":"4399:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":471,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4451:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":468,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4439:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":470,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4439:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4439:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":473,"nodeType":"ExpressionStatement","src":"4439:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":481,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4512:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":480,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4504:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":479,"name":"uint160","nodeType":"ElementaryTypeName","src":"4504:7:0","typeDescriptions":{}}},"id":482,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4504:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":478,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4496:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":477,"name":"address","nodeType":"ElementaryTypeName","src":"4496:7:0","typeDescriptions":{}}},"id":483,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4496:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":474,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4478:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":476,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4478:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4478:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":485,"nodeType":"ExpressionStatement","src":"4478:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":489,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4544:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":486,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4531:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":488,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":678,"src":"4531:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":490,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4531:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":491,"nodeType":"ExpressionStatement","src":"4531:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":492,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4563:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":494,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4563:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4563:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":496,"nodeType":"ExpressionStatement","src":"4563:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":500,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4604:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":497,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4592:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":499,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4592:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4592:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":502,"nodeType":"ExpressionStatement","src":"4592:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":510,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4665:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4657:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":508,"name":"uint160","nodeType":"ElementaryTypeName","src":"4657:7:0","typeDescriptions":{}}},"id":511,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":507,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4649:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":506,"name":"address","nodeType":"ElementaryTypeName","src":"4649:7:0","typeDescriptions":{}}},"id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4649:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":503,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4636:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":505,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4636:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":513,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4636:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":514,"nodeType":"ExpressionStatement","src":"4636:40:0"},{"assignments":[517],"declarations":[{"constant":false,"id":517,"mutability":"mutable","name":"x","nameLocation":"4693:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4686:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":516,"nodeType":"UserDefinedTypeName","pathNode":{"id":515,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4686:6:0"},"referencedDeclaration":719,"src":"4686:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":523,"initialValue":{"arguments":[{"hexValue":"31323334","id":521,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4708:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":520,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4697:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":519,"nodeType":"UserDefinedTypeName","pathNode":{"id":518,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4701:6:0"},"referencedDeclaration":719,"src":"4701:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":522,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4697:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4686:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":529,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":525,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":517,"src":"4731:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":526,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4731:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4731:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":528,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4742:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4731:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":524,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4723:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":530,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4723:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":531,"nodeType":"ExpressionStatement","src":"4723:24:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":535,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4770:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":532,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4758:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":534,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4758:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":536,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4758:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":537,"nodeType":"ExpressionStatement","src":"4758:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":545,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4820:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":544,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4812:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":543,"name":"uint160","nodeType":"ElementaryTypeName","src":"4812:7:0","typeDescriptions":{}}},"id":546,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4812:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":542,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4804:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":541,"name":"address","nodeType":"ElementaryTypeName","src":"4804:7:0","typeDescriptions":{}}},"id":547,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4804:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":538,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4791:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":540,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4791:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4791:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":549,"nodeType":"ExpressionStatement","src":"4791:38:0"},{"assignments":[552],"declarations":[{"constant":false,"id":552,"mutability":"mutable","name":"y","nameLocation":"4846:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4839:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":551,"nodeType":"UserDefinedTypeName","pathNode":{"id":550,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4839:6:0"},"referencedDeclaration":719,"src":"4839:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":566,"initialValue":{"arguments":[{"hexValue":"31323334","id":564,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4889:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":555,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4850:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":554,"nodeType":"UserDefinedTypeName","pathNode":{"id":553,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4854:6:0"},"referencedDeclaration":719,"src":"4854:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":563,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4883:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":559,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4875:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":558,"name":"uint256","nodeType":"ElementaryTypeName","src":"4875:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4875:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":557,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4867:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":556,"name":"bytes32","nodeType":"ElementaryTypeName","src":"4867:7:0","typeDescriptions":{}}},"id":562,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4867:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"4850:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":565,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4850:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4839:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":568,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":552,"src":"4912:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":569,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4912:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":570,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4912:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":571,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4923:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4912:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":567,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4904:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4904:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"4904:24:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4950:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4938:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4938:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4938:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"4938:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5042:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5042:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":584,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5042:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":585,"nodeType":"ExpressionStatement","src":"5042:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5077:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":588,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5066:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":587,"nodeType":"UserDefinedTypeName","pathNode":{"id":586,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"5070:6:0"},"referencedDeclaration":719,"src":"5070:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":590,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5066:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":591,"nodeType":"ExpressionStatement","src":"5066:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":595,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5105:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":602,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5146:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":601,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5138:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":600,"name":"address","nodeType":"ElementaryTypeName","src":"5138:7:0","typeDescriptions":{}}},"id":603,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5138:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":598,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5126:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":599,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5126:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5126:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":597,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5118:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":596,"name":"uint256","nodeType":"ElementaryTypeName","src":"5118:7:0","typeDescriptions":{}}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5118:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":592,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5093:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":594,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"5093:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":606,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5093:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":607,"nodeType":"ExpressionStatement","src":"5093:61:0"}]},"documentation":{"id":379,"nodeType":"StructuredDocumentation","src":"3842:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"3912:12:0","parameters":{"id":380,"nodeType":"ParameterList","parameters":[],"src":"3924:2:0"},"returnParameters":{"id":381,"nodeType":"ParameterList","parameters":[],"src":"3934:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":633,"nodeType":"FunctionDefinition","src":"5256:143:0","nodes":[],"body":{"id":632,"nodeType":"Block","src":"5305:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":618,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":612,"src":"5327:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":615,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5315:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":617,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5315:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5315:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":620,"nodeType":"ExpressionStatement","src":"5315:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5352:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":627,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"5380:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":628,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"5380:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":626,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5372:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":625,"name":"address","nodeType":"ElementaryTypeName","src":"5372:7:0","typeDescriptions":{}}},"id":629,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5372:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":621,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5340:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":623,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"5340:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":630,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5340:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":631,"nodeType":"ExpressionStatement","src":"5340:52:0"}]},"documentation":{"id":610,"nodeType":"StructuredDocumentation","src":"5167:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"5265:5:0","parameters":{"id":613,"nodeType":"ParameterList","parameters":[{"constant":false,"id":612,"mutability":"mutable","name":"_v","nameLocation":"5287:2:0","nodeType":"VariableDeclaration","scope":633,"src":"5271:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":611,"name":"string","nodeType":"ElementaryTypeName","src":"5271:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5270:20:0"},"returnParameters":{"id":614,"nodeType":"ParameterList","parameters":[],"src":"5305:0:0"},"scope":706,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":648,"nodeType":"FunctionDefinition","src":"5405:95:0","nodes":[],"body":{"id":647,"nodeType":"Block","src":"5449:51:0","nodes":[],"statements":[{"expression":{"id":639,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5459:9:0","subExpression":{"id":638,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5459:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":640,"nodeType":"ExpressionStatement","src":"5459:9:0"},{"expression":{"arguments":[{"id":644,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":635,"src":"5490:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":641,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5478:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":643,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5478:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":645,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5478:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":646,"nodeType":"ExpressionStatement","src":"5478:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"5414:5:0","parameters":{"id":636,"nodeType":"ParameterList","parameters":[{"constant":false,"id":635,"mutability":"mutable","name":"_v","nameLocation":"5436:2:0","nodeType":"VariableDeclaration","scope":648,"src":"5420:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":634,"name":"string","nodeType":"ElementaryTypeName","src":"5420:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5419:20:0"},"returnParameters":{"id":637,"nodeType":"ParameterList","parameters":[],"src":"5449:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":663,"nodeType":"FunctionDefinition","src":"5506:95:0","nodes":[],"body":{"id":662,"nodeType":"Block","src":"5550:51:0","nodes":[],"statements":[{"expression":{"id":654,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5560:9:0","subExpression":{"id":653,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5560:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":655,"nodeType":"ExpressionStatement","src":"5560:9:0"},{"expression":{"arguments":[{"id":659,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":650,"src":"5591:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":656,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5579:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":658,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5579:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":660,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5579:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":661,"nodeType":"ExpressionStatement","src":"5579:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"5515:5:0","parameters":{"id":651,"nodeType":"ParameterList","parameters":[{"constant":false,"id":650,"mutability":"mutable","name":"_v","nameLocation":"5537:2:0","nodeType":"VariableDeclaration","scope":663,"src":"5521:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":649,"name":"string","nodeType":"ElementaryTypeName","src":"5521:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5520:20:0"},"returnParameters":{"id":652,"nodeType":"ParameterList","parameters":[],"src":"5550:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":678,"nodeType":"FunctionDefinition","src":"5607:98:0","nodes":[],"body":{"id":677,"nodeType":"Block","src":"5653:52:0","nodes":[],"statements":[{"expression":{"id":669,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5663:9:0","subExpression":{"id":668,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5663:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":670,"nodeType":"ExpressionStatement","src":"5663:9:0"},{"expression":{"arguments":[{"id":674,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":665,"src":"5695:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":671,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5682:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":673,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":693,"src":"5682:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":675,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5682:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":676,"nodeType":"ExpressionStatement","src":"5682:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"5616:7:0","parameters":{"id":666,"nodeType":"ParameterList","parameters":[{"constant":false,"id":665,"mutability":"mutable","name":"_v","nameLocation":"5640:2:0","nodeType":"VariableDeclaration","scope":678,"src":"5624:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":664,"name":"string","nodeType":"ElementaryTypeName","src":"5624:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5623:20:0"},"returnParameters":{"id":667,"nodeType":"ParameterList","parameters":[],"src":"5653:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":693,"nodeType":"FunctionDefinition","src":"5711:97:0","nodes":[],"body":{"id":692,"nodeType":"Block","src":"5757:51:0","nodes":[],"statements":[{"expression":{"id":684,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5767:9:0","subExpression":{"id":683,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5767:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":685,"nodeType":"ExpressionStatement","src":"5767:9:0"},{"expression":{"arguments":[{"id":689,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":680,"src":"5798:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":686,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5786:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":688,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5786:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":690,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5786:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":691,"nodeType":"ExpressionStatement","src":"5786:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"5720:7:0","parameters":{"id":681,"nodeType":"ParameterList","parameters":[{"constant":false,"id":680,"mutability":"mutable","name":"_v","nameLocation":"5744:2:0","nodeType":"VariableDeclaration","scope":693,"src":"5728:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":679,"name":"string","nodeType":"ElementaryTypeName","src":"5728:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5727:20:0"},"returnParameters":{"id":682,"nodeType":"ParameterList","parameters":[],"src":"5757:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":705,"nodeType":"FunctionDefinition","src":"5814:84:0","nodes":[],"body":{"id":704,"nodeType":"Block","src":"5866:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":701,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":695,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":698,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5876:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":700,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5876:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":702,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5876:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":703,"nodeType":"ExpressionStatement","src":"5876:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"5823:8:0","parameters":{"id":696,"nodeType":"ParameterList","parameters":[{"constant":false,"id":695,"mutability":"mutable","name":"_v","nameLocation":"5848:2:0","nodeType":"VariableDeclaration","scope":705,"src":"5832:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":694,"name":"string","nodeType":"ElementaryTypeName","src":"5832:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5831:20:0"},"returnParameters":{"id":697,"nodeType":"ParameterList","parameters":[],"src":"5866:0:0"},"scope":706,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[719],"contractKind":"contract","documentation":{"id":193,"nodeType":"StructuredDocumentation","src":"2415:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[706],"name":"ScriptExample","nameLocation":"2550:13:0","scope":720,"usedErrors":[]},{"id":719,"nodeType":"ContractDefinition","src":"5902:96:0","nodes":[{"id":708,"nodeType":"VariableDeclaration","src":"5924:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"5939:3:0","scope":719,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":707,"name":"uint256","nodeType":"ElementaryTypeName","src":"5924:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":718,"nodeType":"FunctionDefinition","src":"5949:47:0","nodes":[],"body":{"id":717,"nodeType":"Block","src":"5972:24:0","nodes":[],"statements":[{"expression":{"id":715,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":713,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":708,"src":"5982:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":714,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":710,"src":"5988:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"5982:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":716,"nodeType":"ExpressionStatement","src":"5982:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":711,"nodeType":"ParameterList","parameters":[{"constant":false,"id":710,"mutability":"mutable","name":"v","nameLocation":"5969:1:0","nodeType":"VariableDeclaration","scope":718,"src":"5961:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":709,"name":"uint256","nodeType":"ElementaryTypeName","src":"5961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"5960:11:0"},"returnParameters":{"id":712,"nodeType":"ParameterList","parameters":[],"src":"5972:0:0"},"scope":719,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[719],"name":"FooBar","nameLocation":"5911:6:0","scope":720,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
+{"abi":[{"type":"function","name":"allowCheatcodes","inputs":[{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"broadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"broadcast","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"createSelectFork","inputs":[{"name":"forkName","type":"string","internalType":"string"},{"name":"blockNumber","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"envOr","inputs":[{"name":"name","type":"string","internalType":"string"},{"name":"defaultValue","type":"bool","internalType":"bool"}],"outputs":[{"name":"value","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"etch","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"newRuntimeBytecode","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getDeployedCode","inputs":[{"name":"artifactPath","type":"string","internalType":"string"}],"outputs":[{"name":"runtimeBytecode","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getNonce","inputs":[{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"nonce","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"parseJsonKeys","inputs":[{"name":"json","type":"string","internalType":"string"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"keys","type":"string[]","internalType":"string[]"}],"stateMutability":"pure"},{"type":"function","name":"startBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"startBroadcast","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"startPrank","inputs":[{"name":"msgSender","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stopBroadcast","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stopPrank","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"allowCheatcodes(address)":"ea060291","broadcast()":"afc98040","broadcast(address)":"e6962cdb","createSelectFork(string,uint256)":"71ee464d","envOr(string,bool)":"4777f3cf","etch(address,bytes)":"b4d6c782","getDeployedCode(string)":"3ebf73b4","getNonce(address)":"2d0335ab","parseJsonKeys(string,string)":"213e4198","startBroadcast()":"7fb5297f","startBroadcast(address)":"7fec2a8d","startPrank(address)":"06447d56","stopBroadcast()":"76eadd36","stopPrank()":"90c5013b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"allowCheatcodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"broadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"broadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"forkName\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"createSelectFork\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"defaultValue\",\"type\":\"bool\"}],\"name\":\"envOr\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"value\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newRuntimeBytecode\",\"type\":\"bytes\"}],\"name\":\"etch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"artifactPath\",\"type\":\"string\"}],\"name\":\"getDeployedCode\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"runtimeBytecode\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"json\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"}],\"name\":\"parseJsonKeys\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"keys\",\"type\":\"string[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"startBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"startBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"}],\"name\":\"startPrank\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stopBroadcast\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stopPrank\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"Vm\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"allowCheatcodes"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"broadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"broadcast"},{"inputs":[{"internalType":"string","name":"forkName","type":"string"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"createSelectFork","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"bool","name":"defaultValue","type":"bool"}],"stateMutability":"view","type":"function","name":"envOr","outputs":[{"internalType":"bool","name":"value","type":"bool"}]},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"newRuntimeBytecode","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"etch"},{"inputs":[{"internalType":"string","name":"artifactPath","type":"string"}],"stateMutability":"view","type":"function","name":"getDeployedCode","outputs":[{"internalType":"bytes","name":"runtimeBytecode","type":"bytes"}]},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"getNonce","outputs":[{"internalType":"uint64","name":"nonce","type":"uint64"}]},{"inputs":[{"internalType":"string","name":"json","type":"string"},{"internalType":"string","name":"key","type":"string"}],"stateMutability":"pure","type":"function","name":"parseJsonKeys","outputs":[{"internalType":"string[]","name":"keys","type":"string[]"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"startBroadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"startBroadcast"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"startPrank"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"stopBroadcast"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"stopPrank"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"Vm"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/console.json b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/console.json
index 390e798a4701..6ef800dd39d5 100644
--- a/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/console.json
+++ b/op-chain-ops/script/testdata/test-artifacts/ScriptExample.s.sol/console.json
@@ -1 +1 @@
-{"abi":[],"bytecode":{"object":"0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c634300080f000a","sourceMap":"791:1622:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;791:1622:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c634300080f000a","sourceMap":"791:1622:0:-:0;;;;;;;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"console\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3\",\"dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"console"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x8d1dfa41908e7ccc3a498a2a2aa51c5275bedbb904ce32d08f8598e36f896d8d","urls":["bzz-raw://5117bb7158363cae8b9dc0508d2852692fd36172f1c699ff680afbb5acebe1f3","dweb:/ipfs/QmQdahJ8SPKfJ4yea5Ge9qaj5qh1TxVffhHvaWytBaL95h"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":720,"exportedSymbols":{"FooBar":[719],"ScriptExample":[706],"Vm":[55],"console":[192]},"nodeType":"SourceUnit","src":"32:5967:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":55,"nodeType":"ContractDefinition","src":"120:616:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":55,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":55,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":55,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[55],"name":"Vm","nameLocation":"130:2:0","scope":720,"usedErrors":[]},{"id":192,"nodeType":"ContractDefinition","src":"791:1622:0","nodes":[{"id":61,"nodeType":"VariableDeclaration","src":"813:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"830:15:0","scope":192,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":56,"name":"address","nodeType":"ElementaryTypeName","src":"813:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":59,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"856:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":58,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"848:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":57,"name":"address","nodeType":"ElementaryTypeName","src":"848:7:0","typeDescriptions":{}}},"id":60,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"848:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":78,"nodeType":"FunctionDefinition","src":"906:221:0","nodes":[],"body":{"id":77,"nodeType":"Block","src":"1065:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1084:37:0","statements":[{"nodeType":"YulAssignment","src":"1098:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1107:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1098:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":67,"isOffset":false,"isSlot":false,"src":"1107:4:0","valueSize":1},{"declaration":74,"isOffset":false,"isSlot":false,"src":"1098:5:0","valueSize":1}],"id":76,"nodeType":"InlineAssembly","src":"1075:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"915:25:0","parameters":{"id":68,"nodeType":"ParameterList","parameters":[{"constant":false,"id":67,"mutability":"mutable","name":"fnIn","nameLocation":"987:4:0","nodeType":"VariableDeclaration","scope":78,"src":"950:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":66,"nodeType":"FunctionTypeName","parameterTypes":{"id":64,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":66,"src":"959:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":62,"name":"bytes","nodeType":"ElementaryTypeName","src":"959:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"958:14:0"},"returnParameterTypes":{"id":65,"nodeType":"ParameterList","parameters":[],"src":"987:0:0"},"src":"950:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"940:57:0"},"returnParameters":{"id":75,"nodeType":"ParameterList","parameters":[{"constant":false,"id":74,"mutability":"mutable","name":"fnOut","nameLocation":"1058:5:0","nodeType":"VariableDeclaration","scope":78,"src":"1021:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":73,"nodeType":"FunctionTypeName","parameterTypes":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":73,"src":"1030:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":69,"name":"bytes","nodeType":"ElementaryTypeName","src":"1030:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1029:14:0"},"returnParameterTypes":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"1058:0:0"},"src":"1021:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1020:44:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":90,"nodeType":"FunctionDefinition","src":"1133:133:0","nodes":[],"body":{"id":89,"nodeType":"Block","src":"1194:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":86,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":80,"src":"1251:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":84,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1230:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":83,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":78,"src":"1204:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":85,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":87,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1204:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":88,"nodeType":"ExpressionStatement","src":"1204:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1142:15:0","parameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"payload","nameLocation":"1171:7:0","nodeType":"VariableDeclaration","scope":90,"src":"1158:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":79,"name":"bytes","nodeType":"ElementaryTypeName","src":"1158:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1157:22:0"},"returnParameters":{"id":82,"nodeType":"ParameterList","parameters":[],"src":"1194:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1272:380:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1336:316:0","nodes":[],"statements":[{"assignments":[96],"declarations":[{"constant":false,"id":96,"mutability":"mutable","name":"payloadLength","nameLocation":"1354:13:0","nodeType":"VariableDeclaration","scope":105,"src":"1346:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":95,"name":"uint256","nodeType":"ElementaryTypeName","src":"1346:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":99,"initialValue":{"expression":{"id":97,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":92,"src":"1370:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":98,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1370:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1346:38:0"},{"assignments":[101],"declarations":[{"constant":false,"id":101,"mutability":"mutable","name":"consoleAddress","nameLocation":"1402:14:0","nodeType":"VariableDeclaration","scope":105,"src":"1394:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":100,"name":"address","nodeType":"ElementaryTypeName","src":"1394:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":103,"initialValue":{"id":102,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":61,"src":"1419:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1394:40:0"},{"AST":{"nodeType":"YulBlock","src":"1496:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1510:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1534:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1543:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1530:3:0"},"nodeType":"YulFunctionCall","src":"1530:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1514:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1559:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1579:3:0"},"nodeType":"YulFunctionCall","src":"1579:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1586:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1602:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1616:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1631:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1634:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1568:10:0"},"nodeType":"YulFunctionCall","src":"1568:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1563:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":101,"isOffset":false,"isSlot":false,"src":"1586:14:0","valueSize":1},{"declaration":92,"isOffset":false,"isSlot":false,"src":"1534:7:0","valueSize":1},{"declaration":96,"isOffset":false,"isSlot":false,"src":"1616:13:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1487:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1281:19:0","parameters":{"id":93,"nodeType":"ParameterList","parameters":[{"constant":false,"id":92,"mutability":"mutable","name":"payload","nameLocation":"1314:7:0","nodeType":"VariableDeclaration","scope":106,"src":"1301:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":91,"name":"bytes","nodeType":"ElementaryTypeName","src":"1301:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1300:22:0"},"returnParameters":{"id":94,"nodeType":"ParameterList","parameters":[],"src":"1336:0:0"},"scope":192,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":120,"nodeType":"FunctionDefinition","src":"1658:121:0","nodes":[],"body":{"id":119,"nodeType":"Block","src":"1703:76:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":114,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1753:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":115,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1768:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":112,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1729:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":113,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1729:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":116,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1729:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":111,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1713:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":117,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1713:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":118,"nodeType":"ExpressionStatement","src":"1713:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1667:3:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"p0","nameLocation":"1685:2:0","nodeType":"VariableDeclaration","scope":120,"src":"1671:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":107,"name":"string","nodeType":"ElementaryTypeName","src":"1671:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1670:18:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":137,"nodeType":"FunctionDefinition","src":"1785:139:0","nodes":[],"body":{"id":136,"nodeType":"Block","src":"1839:85:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":130,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"1889:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":131,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":122,"src":"1909:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":132,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":124,"src":"1913:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":128,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"1865:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":129,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"1865:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":133,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1865:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":127,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1849:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":134,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1849:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":135,"nodeType":"ExpressionStatement","src":"1849:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1794:3:0","parameters":{"id":125,"nodeType":"ParameterList","parameters":[{"constant":false,"id":122,"mutability":"mutable","name":"p0","nameLocation":"1812:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1798:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":121,"name":"string","nodeType":"ElementaryTypeName","src":"1798:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":124,"mutability":"mutable","name":"p1","nameLocation":"1821:2:0","nodeType":"VariableDeclaration","scope":137,"src":"1816:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":123,"name":"bool","nodeType":"ElementaryTypeName","src":"1816:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"1797:27:0"},"returnParameters":{"id":126,"nodeType":"ParameterList","parameters":[],"src":"1839:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":154,"nodeType":"FunctionDefinition","src":"1930:145:0","nodes":[],"body":{"id":153,"nodeType":"Block","src":"1987:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":147,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2037:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":148,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":139,"src":"2060:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":149,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":141,"src":"2064:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":145,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2013:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":146,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2013:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":150,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2013:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":144,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"1997:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":151,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1997:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":152,"nodeType":"ExpressionStatement","src":"1997:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"1939:3:0","parameters":{"id":142,"nodeType":"ParameterList","parameters":[{"constant":false,"id":139,"mutability":"mutable","name":"p0","nameLocation":"1957:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1943:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":138,"name":"string","nodeType":"ElementaryTypeName","src":"1943:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":141,"mutability":"mutable","name":"p1","nameLocation":"1969:2:0","nodeType":"VariableDeclaration","scope":154,"src":"1961:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":140,"name":"uint256","nodeType":"ElementaryTypeName","src":"1961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1942:30:0"},"returnParameters":{"id":143,"nodeType":"ParameterList","parameters":[],"src":"1987:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":171,"nodeType":"FunctionDefinition","src":"2081:145:0","nodes":[],"body":{"id":170,"nodeType":"Block","src":"2138:88:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":164,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2188:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":165,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":156,"src":"2211:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":166,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":158,"src":"2215:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":162,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2164:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":163,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2164:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":167,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2164:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":161,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2148:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":168,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2148:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":169,"nodeType":"ExpressionStatement","src":"2148:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2090:3:0","parameters":{"id":159,"nodeType":"ParameterList","parameters":[{"constant":false,"id":156,"mutability":"mutable","name":"p0","nameLocation":"2108:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2094:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":155,"name":"string","nodeType":"ElementaryTypeName","src":"2094:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":158,"mutability":"mutable","name":"p1","nameLocation":"2120:2:0","nodeType":"VariableDeclaration","scope":171,"src":"2112:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":157,"name":"address","nodeType":"ElementaryTypeName","src":"2112:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2093:30:0"},"returnParameters":{"id":160,"nodeType":"ParameterList","parameters":[],"src":"2138:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":191,"nodeType":"FunctionDefinition","src":"2232:179:0","nodes":[],"body":{"id":190,"nodeType":"Block","src":"2313:98:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":183,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2363:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":184,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":173,"src":"2392:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":185,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":175,"src":"2396:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":186,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":177,"src":"2400:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":181,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2339:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":182,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2339:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":187,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2339:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":180,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":90,"src":"2323:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":188,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2323:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":189,"nodeType":"ExpressionStatement","src":"2323:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2241:3:0","parameters":{"id":178,"nodeType":"ParameterList","parameters":[{"constant":false,"id":173,"mutability":"mutable","name":"p0","nameLocation":"2259:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2245:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":172,"name":"string","nodeType":"ElementaryTypeName","src":"2245:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":175,"mutability":"mutable","name":"p1","nameLocation":"2277:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2263:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":174,"name":"string","nodeType":"ElementaryTypeName","src":"2263:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":177,"mutability":"mutable","name":"p2","nameLocation":"2295:2:0","nodeType":"VariableDeclaration","scope":191,"src":"2281:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":176,"name":"string","nodeType":"ElementaryTypeName","src":"2281:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2244:54:0"},"returnParameters":{"id":179,"nodeType":"ParameterList","parameters":[],"src":"2313:0:0"},"scope":192,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[192],"name":"console","nameLocation":"799:7:0","scope":720,"usedErrors":[]},{"id":706,"nodeType":"ContractDefinition","src":"2541:3359:0","nodes":[{"id":207,"nodeType":"VariableDeclaration","src":"2571:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"2597:10:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":194,"name":"address","nodeType":"ElementaryTypeName","src":"2571:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":202,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2644:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":201,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"2634:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":203,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2634:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":200,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2626:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":199,"name":"uint256","nodeType":"ElementaryTypeName","src":"2626:7:0","typeDescriptions":{}}},"id":204,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2626:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":198,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2618:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":197,"name":"uint160","nodeType":"ElementaryTypeName","src":"2618:7:0","typeDescriptions":{}}},"id":205,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2618:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":196,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"2610:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":195,"name":"address","nodeType":"ElementaryTypeName","src":"2610:7:0","typeDescriptions":{}}},"id":206,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2610:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":213,"nodeType":"VariableDeclaration","src":"2671:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"2692:2:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"},"typeName":{"id":209,"nodeType":"UserDefinedTypeName","pathNode":{"id":208,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":55,"src":"2671:2:0"},"referencedDeclaration":55,"src":"2671:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"value":{"arguments":[{"id":211,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":207,"src":"2700:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":210,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":55,"src":"2697:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$55_$","typeString":"type(contract Vm)"}},"id":212,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2697:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"visibility":"internal"},{"id":215,"nodeType":"VariableDeclaration","src":"2775:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"2790:7:0","scope":706,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":214,"name":"uint256","nodeType":"ElementaryTypeName","src":"2775:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":378,"nodeType":"FunctionDefinition","src":"2887:949:0","nodes":[],"body":{"id":377,"nodeType":"Block","src":"2909:927:0","nodes":[],"statements":[{"assignments":[220],"declarations":[{"constant":false,"id":220,"mutability":"mutable","name":"x","nameLocation":"2924:1:0","nodeType":"VariableDeclaration","scope":377,"src":"2919:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":219,"name":"bool","nodeType":"ElementaryTypeName","src":"2919:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":226,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":223,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2937:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":224,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"2953:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":221,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"2928:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":222,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"2928:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":225,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2928:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"2919:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2981:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":231,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3004:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":227,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"2969:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":229,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":137,"src":"2969:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":232,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2969:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":233,"nodeType":"ExpressionStatement","src":"2969:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":237,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3029:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":240,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3054:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":239,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3046:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":238,"name":"address","nodeType":"ElementaryTypeName","src":"3046:7:0","typeDescriptions":{}}},"id":241,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3046:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":234,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3017:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":236,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3017:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":242,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3017:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":243,"nodeType":"ExpressionStatement","src":"3017:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":247,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3082:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":252,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3120:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":251,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3112:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":250,"name":"address","nodeType":"ElementaryTypeName","src":"3112:7:0","typeDescriptions":{}}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3112:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":248,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3100:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":249,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3100:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":254,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3100:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":244,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3070:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":246,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3070:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":255,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3070:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":256,"nodeType":"ExpressionStatement","src":"3070:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":260,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3149:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":263,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3172:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3172:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":262,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3164:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":261,"name":"address","nodeType":"ElementaryTypeName","src":"3164:7:0","typeDescriptions":{}}},"id":265,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3164:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":257,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3137:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":259,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3137:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":266,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3137:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":267,"nodeType":"ExpressionStatement","src":"3137:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":271,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3206:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":276,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3242:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3242:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":275,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3234:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":274,"name":"address","nodeType":"ElementaryTypeName","src":"3234:7:0","typeDescriptions":{}}},"id":278,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3234:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":272,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3222:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":273,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3222:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":279,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3222:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":268,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3194:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3194:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":280,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3194:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":281,"nodeType":"ExpressionStatement","src":"3194:61:0"},{"assignments":[283],"declarations":[{"constant":false,"id":283,"mutability":"mutable","name":"json","nameLocation":"3280:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3266:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":282,"name":"string","nodeType":"ElementaryTypeName","src":"3266:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":285,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":284,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3287:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3266:55:0"},{"assignments":[290],"declarations":[{"constant":false,"id":290,"mutability":"mutable","name":"keys","nameLocation":"3347:4:0","nodeType":"VariableDeclaration","scope":377,"src":"3331:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":288,"name":"string","nodeType":"ElementaryTypeName","src":"3331:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":289,"nodeType":"ArrayTypeName","src":"3331:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":296,"initialValue":{"arguments":[{"id":293,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":283,"src":"3371:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":294,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3377:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":291,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3354:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3354:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":295,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3354:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3331:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":300,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3414:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":301,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3422:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":303,"indexExpression":{"hexValue":"30","id":302,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3427:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3422:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":304,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":290,"src":"3431:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":306,"indexExpression":{"hexValue":"31","id":305,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3436:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"3431:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":297,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3402:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":299,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":191,"src":"3402:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3402:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":308,"nodeType":"ExpressionStatement","src":"3402:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3461:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":309,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3450:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":311,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3450:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":313,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3450:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":314,"nodeType":"ExpressionStatement","src":"3450:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"3517:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":321,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3509:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":320,"name":"uint160","nodeType":"ElementaryTypeName","src":"3509:7:0","typeDescriptions":{}}},"id":323,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":319,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3501:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":318,"name":"address","nodeType":"ElementaryTypeName","src":"3501:7:0","typeDescriptions":{}}},"id":324,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3501:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":315,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3487:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":317,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"3487:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":325,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3487:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":326,"nodeType":"ExpressionStatement","src":"3487:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3545:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":327,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3534:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":329,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3534:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":331,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3534:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":332,"nodeType":"ExpressionStatement","src":"3534:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":336,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3582:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":339,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3617:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":340,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3617:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":338,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3609:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":337,"name":"address","nodeType":"ElementaryTypeName","src":"3609:7:0","typeDescriptions":{}}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3609:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":333,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3570:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3570:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":342,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3570:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":343,"nodeType":"ExpressionStatement","src":"3570:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":347,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3651:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":350,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3689:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3681:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":348,"name":"address","nodeType":"ElementaryTypeName","src":"3681:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":344,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3639:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":346,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"3639:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":352,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3639:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":353,"nodeType":"ExpressionStatement","src":"3639:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":357,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3716:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":354,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3705:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":356,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3705:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":358,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3705:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":359,"nodeType":"ExpressionStatement","src":"3705:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":360,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3741:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":362,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"3741:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3741:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":364,"nodeType":"ExpressionStatement","src":"3741:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":368,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3776:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":365,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3765:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":367,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":633,"src":"3765:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3765:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":370,"nodeType":"ExpressionStatement","src":"3765:33:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":374,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3821:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":371,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3809:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":373,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"3809:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":375,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3809:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":376,"nodeType":"ExpressionStatement","src":"3809:20:0"}]},"documentation":{"id":216,"nodeType":"StructuredDocumentation","src":"2804:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"2896:3:0","parameters":{"id":217,"nodeType":"ParameterList","parameters":[],"src":"2899:2:0"},"returnParameters":{"id":218,"nodeType":"ParameterList","parameters":[],"src":"2909:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":609,"nodeType":"FunctionDefinition","src":"3903:1258:0","nodes":[],"body":{"id":608,"nodeType":"Block","src":"3934:1227:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3956:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":392,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3999:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":391,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3991:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":390,"name":"address","nodeType":"ElementaryTypeName","src":"3991:7:0","typeDescriptions":{}}},"id":393,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3991:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"3979:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":389,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3979:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":394,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3979:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":387,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3971:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":386,"name":"uint256","nodeType":"ElementaryTypeName","src":"3971:7:0","typeDescriptions":{}}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3971:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":382,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"3944:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"3944:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":396,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3944:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":397,"nodeType":"ExpressionStatement","src":"3944:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":401,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4030:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":398,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4018:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":400,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4018:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":402,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4018:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":403,"nodeType":"ExpressionStatement","src":"4018:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":404,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4057:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":406,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"4057:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":407,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4057:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":408,"nodeType":"ExpressionStatement","src":"4057:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4092:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":409,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4081:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":411,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4081:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":413,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4081:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":414,"nodeType":"ExpressionStatement","src":"4081:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":418,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4128:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":415,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4117:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":417,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4117:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4117:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":420,"nodeType":"ExpressionStatement","src":"4117:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":424,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4166:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":421,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4154:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":423,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4154:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":425,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4154:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":426,"nodeType":"ExpressionStatement","src":"4154:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":434,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4231:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":433,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4223:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":432,"name":"uint160","nodeType":"ElementaryTypeName","src":"4223:7:0","typeDescriptions":{}}},"id":435,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4223:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":431,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4215:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":430,"name":"address","nodeType":"ElementaryTypeName","src":"4215:7:0","typeDescriptions":{}}},"id":436,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4215:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":427,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4197:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":429,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4197:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":437,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4197:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":438,"nodeType":"ExpressionStatement","src":"4197:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":442,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4263:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":439,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4252:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":441,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4252:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":443,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4252:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":444,"nodeType":"ExpressionStatement","src":"4252:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":448,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4302:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":445,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4291:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":663,"src":"4291:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":449,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4291:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":450,"nodeType":"ExpressionStatement","src":"4291:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":454,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4344:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":451,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4330:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":705,"src":"4330:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":455,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4330:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":456,"nodeType":"ExpressionStatement","src":"4330:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":457,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4371:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":459,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4371:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":460,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4371:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":461,"nodeType":"ExpressionStatement","src":"4371:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":465,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4410:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":462,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4399:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":464,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":648,"src":"4399:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":466,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4399:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":467,"nodeType":"ExpressionStatement","src":"4399:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":471,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4451:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":468,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4439:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":470,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4439:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4439:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":473,"nodeType":"ExpressionStatement","src":"4439:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":481,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4512:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":480,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4504:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":479,"name":"uint160","nodeType":"ElementaryTypeName","src":"4504:7:0","typeDescriptions":{}}},"id":482,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4504:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":478,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4496:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":477,"name":"address","nodeType":"ElementaryTypeName","src":"4496:7:0","typeDescriptions":{}}},"id":483,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4496:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":474,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4478:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":476,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"4478:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4478:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":485,"nodeType":"ExpressionStatement","src":"4478:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":489,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4544:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":486,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4531:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":488,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":678,"src":"4531:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":490,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4531:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":491,"nodeType":"ExpressionStatement","src":"4531:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":492,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4563:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":494,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"4563:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4563:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":496,"nodeType":"ExpressionStatement","src":"4563:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":500,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4604:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":497,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4592:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":499,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4592:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4592:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":502,"nodeType":"ExpressionStatement","src":"4592:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":510,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4665:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4657:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":508,"name":"uint160","nodeType":"ElementaryTypeName","src":"4657:7:0","typeDescriptions":{}}},"id":511,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":507,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4649:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":506,"name":"address","nodeType":"ElementaryTypeName","src":"4649:7:0","typeDescriptions":{}}},"id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4649:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":503,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4636:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":505,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4636:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":513,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4636:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":514,"nodeType":"ExpressionStatement","src":"4636:40:0"},{"assignments":[517],"declarations":[{"constant":false,"id":517,"mutability":"mutable","name":"x","nameLocation":"4693:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4686:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":516,"nodeType":"UserDefinedTypeName","pathNode":{"id":515,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4686:6:0"},"referencedDeclaration":719,"src":"4686:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":523,"initialValue":{"arguments":[{"hexValue":"31323334","id":521,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4708:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":520,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4697:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":519,"nodeType":"UserDefinedTypeName","pathNode":{"id":518,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4701:6:0"},"referencedDeclaration":719,"src":"4701:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":522,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4697:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4686:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":529,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":525,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":517,"src":"4731:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":526,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4731:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4731:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":528,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4742:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4731:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":524,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4723:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":530,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4723:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":531,"nodeType":"ExpressionStatement","src":"4723:24:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":535,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4770:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":532,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4758:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":534,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4758:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":536,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4758:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":537,"nodeType":"ExpressionStatement","src":"4758:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":545,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4820:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":544,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4812:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":543,"name":"uint160","nodeType":"ElementaryTypeName","src":"4812:7:0","typeDescriptions":{}}},"id":546,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4812:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":542,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4804:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":541,"name":"address","nodeType":"ElementaryTypeName","src":"4804:7:0","typeDescriptions":{}}},"id":547,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4804:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":538,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"4791:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":540,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"4791:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4791:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":549,"nodeType":"ExpressionStatement","src":"4791:38:0"},{"assignments":[552],"declarations":[{"constant":false,"id":552,"mutability":"mutable","name":"y","nameLocation":"4846:1:0","nodeType":"VariableDeclaration","scope":608,"src":"4839:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"},"typeName":{"id":551,"nodeType":"UserDefinedTypeName","pathNode":{"id":550,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4839:6:0"},"referencedDeclaration":719,"src":"4839:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"visibility":"internal"}],"id":566,"initialValue":{"arguments":[{"hexValue":"31323334","id":564,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4889:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":555,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"4850:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":554,"nodeType":"UserDefinedTypeName","pathNode":{"id":553,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"4854:6:0"},"referencedDeclaration":719,"src":"4854:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":563,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4883:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":559,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4875:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":558,"name":"uint256","nodeType":"ElementaryTypeName","src":"4875:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4875:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":557,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4867:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":556,"name":"bytes32","nodeType":"ElementaryTypeName","src":"4867:7:0","typeDescriptions":{}}},"id":562,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4867:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"4850:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":565,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4850:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"4839:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":568,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":552,"src":"4912:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":569,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":708,"src":"4912:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":570,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4912:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":571,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4923:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"4912:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"}],"id":567,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"4904:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$returns$__$","typeString":"function (bool) pure"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4904:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"4904:24:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4950:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"4938:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"4938:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4938:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"4938:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5042:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5042:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":584,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5042:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":585,"nodeType":"ExpressionStatement","src":"5042:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5077:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":588,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5066:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$719_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":587,"nodeType":"UserDefinedTypeName","pathNode":{"id":586,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":719,"src":"5070:6:0"},"referencedDeclaration":719,"src":"5070:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}}},"id":590,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5066:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$719","typeString":"contract FooBar"}},"id":591,"nodeType":"ExpressionStatement","src":"5066:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":595,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5105:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":602,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5146:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}],"id":601,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5138:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":600,"name":"address","nodeType":"ElementaryTypeName","src":"5138:7:0","typeDescriptions":{}}},"id":603,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5138:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":598,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":213,"src":"5126:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$55","typeString":"contract Vm"}},"id":599,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5126:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5126:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":597,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5118:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":596,"name":"uint256","nodeType":"ElementaryTypeName","src":"5118:7:0","typeDescriptions":{}}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5118:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":592,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5093:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":594,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":154,"src":"5093:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":606,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5093:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":607,"nodeType":"ExpressionStatement","src":"5093:61:0"}]},"documentation":{"id":379,"nodeType":"StructuredDocumentation","src":"3842:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"3912:12:0","parameters":{"id":380,"nodeType":"ParameterList","parameters":[],"src":"3924:2:0"},"returnParameters":{"id":381,"nodeType":"ParameterList","parameters":[],"src":"3934:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":633,"nodeType":"FunctionDefinition","src":"5256:143:0","nodes":[],"body":{"id":632,"nodeType":"Block","src":"5305:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":618,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":612,"src":"5327:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":615,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5315:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":617,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5315:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5315:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":620,"nodeType":"ExpressionStatement","src":"5315:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5352:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":627,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"5380:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":628,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"5380:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":626,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5372:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":625,"name":"address","nodeType":"ElementaryTypeName","src":"5372:7:0","typeDescriptions":{}}},"id":629,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5372:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":621,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5340:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":623,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":171,"src":"5340:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":630,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5340:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":631,"nodeType":"ExpressionStatement","src":"5340:52:0"}]},"documentation":{"id":610,"nodeType":"StructuredDocumentation","src":"5167:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"5265:5:0","parameters":{"id":613,"nodeType":"ParameterList","parameters":[{"constant":false,"id":612,"mutability":"mutable","name":"_v","nameLocation":"5287:2:0","nodeType":"VariableDeclaration","scope":633,"src":"5271:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":611,"name":"string","nodeType":"ElementaryTypeName","src":"5271:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5270:20:0"},"returnParameters":{"id":614,"nodeType":"ParameterList","parameters":[],"src":"5305:0:0"},"scope":706,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":648,"nodeType":"FunctionDefinition","src":"5405:95:0","nodes":[],"body":{"id":647,"nodeType":"Block","src":"5449:51:0","nodes":[],"statements":[{"expression":{"id":639,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5459:9:0","subExpression":{"id":638,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5459:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":640,"nodeType":"ExpressionStatement","src":"5459:9:0"},{"expression":{"arguments":[{"id":644,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":635,"src":"5490:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":641,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5478:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":643,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5478:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":645,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5478:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":646,"nodeType":"ExpressionStatement","src":"5478:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"5414:5:0","parameters":{"id":636,"nodeType":"ParameterList","parameters":[{"constant":false,"id":635,"mutability":"mutable","name":"_v","nameLocation":"5436:2:0","nodeType":"VariableDeclaration","scope":648,"src":"5420:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":634,"name":"string","nodeType":"ElementaryTypeName","src":"5420:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5419:20:0"},"returnParameters":{"id":637,"nodeType":"ParameterList","parameters":[],"src":"5449:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":663,"nodeType":"FunctionDefinition","src":"5506:95:0","nodes":[],"body":{"id":662,"nodeType":"Block","src":"5550:51:0","nodes":[],"statements":[{"expression":{"id":654,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5560:9:0","subExpression":{"id":653,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5560:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":655,"nodeType":"ExpressionStatement","src":"5560:9:0"},{"expression":{"arguments":[{"id":659,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":650,"src":"5591:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":656,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5579:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":658,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5579:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":660,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5579:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":661,"nodeType":"ExpressionStatement","src":"5579:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"5515:5:0","parameters":{"id":651,"nodeType":"ParameterList","parameters":[{"constant":false,"id":650,"mutability":"mutable","name":"_v","nameLocation":"5537:2:0","nodeType":"VariableDeclaration","scope":663,"src":"5521:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":649,"name":"string","nodeType":"ElementaryTypeName","src":"5521:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5520:20:0"},"returnParameters":{"id":652,"nodeType":"ParameterList","parameters":[],"src":"5550:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":678,"nodeType":"FunctionDefinition","src":"5607:98:0","nodes":[],"body":{"id":677,"nodeType":"Block","src":"5653:52:0","nodes":[],"statements":[{"expression":{"id":669,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5663:9:0","subExpression":{"id":668,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5663:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":670,"nodeType":"ExpressionStatement","src":"5663:9:0"},{"expression":{"arguments":[{"id":674,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":665,"src":"5695:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":671,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5682:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$706","typeString":"contract ScriptExample"}},"id":673,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":693,"src":"5682:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":675,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5682:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":676,"nodeType":"ExpressionStatement","src":"5682:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"5616:7:0","parameters":{"id":666,"nodeType":"ParameterList","parameters":[{"constant":false,"id":665,"mutability":"mutable","name":"_v","nameLocation":"5640:2:0","nodeType":"VariableDeclaration","scope":678,"src":"5624:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":664,"name":"string","nodeType":"ElementaryTypeName","src":"5624:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5623:20:0"},"returnParameters":{"id":667,"nodeType":"ParameterList","parameters":[],"src":"5653:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":693,"nodeType":"FunctionDefinition","src":"5711:97:0","nodes":[],"body":{"id":692,"nodeType":"Block","src":"5757:51:0","nodes":[],"statements":[{"expression":{"id":684,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"5767:9:0","subExpression":{"id":683,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":215,"src":"5767:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":685,"nodeType":"ExpressionStatement","src":"5767:9:0"},{"expression":{"arguments":[{"id":689,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":680,"src":"5798:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":686,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5786:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":688,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5786:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":690,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5786:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":691,"nodeType":"ExpressionStatement","src":"5786:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"5720:7:0","parameters":{"id":681,"nodeType":"ParameterList","parameters":[{"constant":false,"id":680,"mutability":"mutable","name":"_v","nameLocation":"5744:2:0","nodeType":"VariableDeclaration","scope":693,"src":"5728:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":679,"name":"string","nodeType":"ElementaryTypeName","src":"5728:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5727:20:0"},"returnParameters":{"id":682,"nodeType":"ParameterList","parameters":[],"src":"5757:0:0"},"scope":706,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":705,"nodeType":"FunctionDefinition","src":"5814:84:0","nodes":[],"body":{"id":704,"nodeType":"Block","src":"5866:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":701,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":695,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":698,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":192,"src":"5876:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$192_$","typeString":"type(library console)"}},"id":700,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":120,"src":"5876:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":702,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5876:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":703,"nodeType":"ExpressionStatement","src":"5876:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"5823:8:0","parameters":{"id":696,"nodeType":"ParameterList","parameters":[{"constant":false,"id":695,"mutability":"mutable","name":"_v","nameLocation":"5848:2:0","nodeType":"VariableDeclaration","scope":705,"src":"5832:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":694,"name":"string","nodeType":"ElementaryTypeName","src":"5832:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"5831:20:0"},"returnParameters":{"id":697,"nodeType":"ParameterList","parameters":[],"src":"5866:0:0"},"scope":706,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[719],"contractKind":"contract","documentation":{"id":193,"nodeType":"StructuredDocumentation","src":"2415:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[706],"name":"ScriptExample","nameLocation":"2550:13:0","scope":720,"usedErrors":[]},{"id":719,"nodeType":"ContractDefinition","src":"5902:96:0","nodes":[{"id":708,"nodeType":"VariableDeclaration","src":"5924:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"5939:3:0","scope":719,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":707,"name":"uint256","nodeType":"ElementaryTypeName","src":"5924:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":718,"nodeType":"FunctionDefinition","src":"5949:47:0","nodes":[],"body":{"id":717,"nodeType":"Block","src":"5972:24:0","nodes":[],"statements":[{"expression":{"id":715,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":713,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":708,"src":"5982:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":714,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":710,"src":"5988:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"5982:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":716,"nodeType":"ExpressionStatement","src":"5982:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":711,"nodeType":"ParameterList","parameters":[{"constant":false,"id":710,"mutability":"mutable","name":"v","nameLocation":"5969:1:0","nodeType":"VariableDeclaration","scope":718,"src":"5961:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":709,"name":"uint256","nodeType":"ElementaryTypeName","src":"5961:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"5960:11:0"},"returnParameters":{"id":712,"nodeType":"ParameterList","parameters":[],"src":"5972:0:0"},"scope":719,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[719],"name":"FooBar","nameLocation":"5911:6:0","scope":720,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
+{"abi":[],"bytecode":{"object":"0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c634300080f000a","sourceMap":"1144:1851:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;1144:1851:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c634300080f000a","sourceMap":"1144:1851:0:-:0;;;;;;;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"scripts/ScriptExample.s.sol\":\"console\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"scripts/ScriptExample.s.sol\":{\"keccak256\":\"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28\",\"dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.15+commit.e14f2714"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"scripts/ScriptExample.s.sol":"console"},"evmVersion":"london","libraries":{}},"sources":{"scripts/ScriptExample.s.sol":{"keccak256":"0x1fd8237b3b3dff6f5f0dcff6572ad225d40275cdf471b8f6bac1df896c0e56da","urls":["bzz-raw://0e60c01e0c609f4401cb66c7d10819321ca7aec52cfb8b688f57f5ae54ee9f28","dweb:/ipfs/QmXyqERiuKiVaUWmP4XVZXdJvhoPsFvbySF2WWJtKjgSy8"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"scripts/ScriptExample.s.sol","id":969,"exportedSymbols":{"FooBar":[799],"ForkTester":[968],"ForkedContract":[852],"NonceGetter":[833],"ScriptExample":[786],"Vm":[83],"console":[220]},"nodeType":"SourceUnit","src":"32:8375:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".15"]},{"id":83,"nodeType":"ContractDefinition","src":"120:969:0","nodes":[{"id":10,"nodeType":"FunctionDefinition","src":"139:91:0","nodes":[],"functionSelector":"4777f3cf","implemented":false,"kind":"function","modifiers":[],"name":"envOr","nameLocation":"148:5:0","parameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":3,"mutability":"mutable","name":"name","nameLocation":"170:4:0","nodeType":"VariableDeclaration","scope":10,"src":"154:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":2,"name":"string","nodeType":"ElementaryTypeName","src":"154:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":5,"mutability":"mutable","name":"defaultValue","nameLocation":"181:12:0","nodeType":"VariableDeclaration","scope":10,"src":"176:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":4,"name":"bool","nodeType":"ElementaryTypeName","src":"176:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"153:41:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[{"constant":false,"id":8,"mutability":"mutable","name":"value","nameLocation":"223:5:0","nodeType":"VariableDeclaration","scope":10,"src":"218:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":7,"name":"bool","nodeType":"ElementaryTypeName","src":"218:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"217:12:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":17,"nodeType":"FunctionDefinition","src":"235:72:0","nodes":[],"functionSelector":"2d0335ab","implemented":false,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"244:8:0","parameters":{"id":13,"nodeType":"ParameterList","parameters":[{"constant":false,"id":12,"mutability":"mutable","name":"account","nameLocation":"261:7:0","nodeType":"VariableDeclaration","scope":17,"src":"253:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":11,"name":"address","nodeType":"ElementaryTypeName","src":"253:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"252:17:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"nonce","nameLocation":"300:5:0","nodeType":"VariableDeclaration","scope":17,"src":"293:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"},"typeName":{"id":14,"name":"uint64","nodeType":"ElementaryTypeName","src":"293:6:0","typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"visibility":"internal"}],"src":"292:14:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":27,"nodeType":"FunctionDefinition","src":"312:111:0","nodes":[],"functionSelector":"213e4198","implemented":false,"kind":"function","modifiers":[],"name":"parseJsonKeys","nameLocation":"321:13:0","parameters":{"id":22,"nodeType":"ParameterList","parameters":[{"constant":false,"id":19,"mutability":"mutable","name":"json","nameLocation":"351:4:0","nodeType":"VariableDeclaration","scope":27,"src":"335:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":18,"name":"string","nodeType":"ElementaryTypeName","src":"335:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":21,"mutability":"mutable","name":"key","nameLocation":"373:3:0","nodeType":"VariableDeclaration","scope":27,"src":"357:19:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":20,"name":"string","nodeType":"ElementaryTypeName","src":"357:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"334:43:0"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[{"constant":false,"id":25,"mutability":"mutable","name":"keys","nameLocation":"417:4:0","nodeType":"VariableDeclaration","scope":27,"src":"401:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"401:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":24,"nodeType":"ArrayTypeName","src":"401:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"src":"400:22:0"},"scope":83,"stateMutability":"pure","virtual":false,"visibility":"external"},{"id":32,"nodeType":"FunctionDefinition","src":"428:48:0","nodes":[],"functionSelector":"06447d56","implemented":false,"kind":"function","modifiers":[],"name":"startPrank","nameLocation":"437:10:0","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":29,"mutability":"mutable","name":"msgSender","nameLocation":"456:9:0","nodeType":"VariableDeclaration","scope":32,"src":"448:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":28,"name":"address","nodeType":"ElementaryTypeName","src":"448:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"447:19:0"},"returnParameters":{"id":31,"nodeType":"ParameterList","parameters":[],"src":"475:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":35,"nodeType":"FunctionDefinition","src":"481:30:0","nodes":[],"functionSelector":"90c5013b","implemented":false,"kind":"function","modifiers":[],"name":"stopPrank","nameLocation":"490:9:0","parameters":{"id":33,"nodeType":"ParameterList","parameters":[],"src":"499:2:0"},"returnParameters":{"id":34,"nodeType":"ParameterList","parameters":[],"src":"510:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":38,"nodeType":"FunctionDefinition","src":"516:30:0","nodes":[],"functionSelector":"afc98040","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"525:9:0","parameters":{"id":36,"nodeType":"ParameterList","parameters":[],"src":"534:2:0"},"returnParameters":{"id":37,"nodeType":"ParameterList","parameters":[],"src":"545:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":43,"nodeType":"FunctionDefinition","src":"551:47:0","nodes":[],"functionSelector":"e6962cdb","implemented":false,"kind":"function","modifiers":[],"name":"broadcast","nameLocation":"560:9:0","parameters":{"id":41,"nodeType":"ParameterList","parameters":[{"constant":false,"id":40,"mutability":"mutable","name":"msgSender","nameLocation":"578:9:0","nodeType":"VariableDeclaration","scope":43,"src":"570:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":39,"name":"address","nodeType":"ElementaryTypeName","src":"570:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"569:19:0"},"returnParameters":{"id":42,"nodeType":"ParameterList","parameters":[],"src":"597:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":48,"nodeType":"FunctionDefinition","src":"603:52:0","nodes":[],"functionSelector":"7fec2a8d","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"612:14:0","parameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"msgSender","nameLocation":"635:9:0","nodeType":"VariableDeclaration","scope":48,"src":"627:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":44,"name":"address","nodeType":"ElementaryTypeName","src":"627:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"626:19:0"},"returnParameters":{"id":47,"nodeType":"ParameterList","parameters":[],"src":"654:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":51,"nodeType":"FunctionDefinition","src":"660:35:0","nodes":[],"functionSelector":"7fb5297f","implemented":false,"kind":"function","modifiers":[],"name":"startBroadcast","nameLocation":"669:14:0","parameters":{"id":49,"nodeType":"ParameterList","parameters":[],"src":"683:2:0"},"returnParameters":{"id":50,"nodeType":"ParameterList","parameters":[],"src":"694:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":54,"nodeType":"FunctionDefinition","src":"700:34:0","nodes":[],"functionSelector":"76eadd36","implemented":false,"kind":"function","modifiers":[],"name":"stopBroadcast","nameLocation":"709:13:0","parameters":{"id":52,"nodeType":"ParameterList","parameters":[],"src":"722:2:0"},"returnParameters":{"id":53,"nodeType":"ParameterList","parameters":[],"src":"733:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":61,"nodeType":"FunctionDefinition","src":"739:108:0","nodes":[],"functionSelector":"3ebf73b4","implemented":false,"kind":"function","modifiers":[],"name":"getDeployedCode","nameLocation":"748:15:0","parameters":{"id":57,"nodeType":"ParameterList","parameters":[{"constant":false,"id":56,"mutability":"mutable","name":"artifactPath","nameLocation":"780:12:0","nodeType":"VariableDeclaration","scope":61,"src":"764:28:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":55,"name":"string","nodeType":"ElementaryTypeName","src":"764:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"763:30:0"},"returnParameters":{"id":60,"nodeType":"ParameterList","parameters":[{"constant":false,"id":59,"mutability":"mutable","name":"runtimeBytecode","nameLocation":"830:15:0","nodeType":"VariableDeclaration","scope":61,"src":"817:28:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":58,"name":"bytes","nodeType":"ElementaryTypeName","src":"817:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"816:30:0"},"scope":83,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":68,"nodeType":"FunctionDefinition","src":"852:74:0","nodes":[],"functionSelector":"b4d6c782","implemented":false,"kind":"function","modifiers":[],"name":"etch","nameLocation":"861:4:0","parameters":{"id":66,"nodeType":"ParameterList","parameters":[{"constant":false,"id":63,"mutability":"mutable","name":"target","nameLocation":"874:6:0","nodeType":"VariableDeclaration","scope":68,"src":"866:14:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":62,"name":"address","nodeType":"ElementaryTypeName","src":"866:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":65,"mutability":"mutable","name":"newRuntimeBytecode","nameLocation":"897:18:0","nodeType":"VariableDeclaration","scope":68,"src":"882:33:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":64,"name":"bytes","nodeType":"ElementaryTypeName","src":"882:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"865:51:0"},"returnParameters":{"id":67,"nodeType":"ParameterList","parameters":[],"src":"925:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":73,"nodeType":"FunctionDefinition","src":"931:51:0","nodes":[],"functionSelector":"ea060291","implemented":false,"kind":"function","modifiers":[],"name":"allowCheatcodes","nameLocation":"940:15:0","parameters":{"id":71,"nodeType":"ParameterList","parameters":[{"constant":false,"id":70,"mutability":"mutable","name":"account","nameLocation":"964:7:0","nodeType":"VariableDeclaration","scope":73,"src":"956:15:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":69,"name":"address","nodeType":"ElementaryTypeName","src":"956:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"955:17:0"},"returnParameters":{"id":72,"nodeType":"ParameterList","parameters":[],"src":"981:0:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":82,"nodeType":"FunctionDefinition","src":"987:100:0","nodes":[],"functionSelector":"71ee464d","implemented":false,"kind":"function","modifiers":[],"name":"createSelectFork","nameLocation":"996:16:0","parameters":{"id":78,"nodeType":"ParameterList","parameters":[{"constant":false,"id":75,"mutability":"mutable","name":"forkName","nameLocation":"1029:8:0","nodeType":"VariableDeclaration","scope":82,"src":"1013:24:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":74,"name":"string","nodeType":"ElementaryTypeName","src":"1013:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":77,"mutability":"mutable","name":"blockNumber","nameLocation":"1047:11:0","nodeType":"VariableDeclaration","scope":82,"src":"1039:19:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":76,"name":"uint256","nodeType":"ElementaryTypeName","src":"1039:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1012:47:0"},"returnParameters":{"id":81,"nodeType":"ParameterList","parameters":[{"constant":false,"id":80,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":82,"src":"1078:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":79,"name":"uint256","nodeType":"ElementaryTypeName","src":"1078:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1077:9:0"},"scope":83,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Vm","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[83],"name":"Vm","nameLocation":"130:2:0","scope":969,"usedErrors":[]},{"id":220,"nodeType":"ContractDefinition","src":"1144:1851:0","nodes":[{"id":89,"nodeType":"VariableDeclaration","src":"1166:86:0","nodes":[],"constant":true,"mutability":"constant","name":"CONSOLE_ADDRESS","nameLocation":"1183:15:0","scope":220,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":84,"name":"address","nodeType":"ElementaryTypeName","src":"1166:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"hexValue":"307830303030303030303030303030303030303036333646366537333646366336353265366336663637","id":87,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"1209:42:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"value":"0x000000000000000000636F6e736F6c652e6c6f67"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":86,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"1201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":85,"name":"address","nodeType":"ElementaryTypeName","src":"1201:7:0","typeDescriptions":{}}},"id":88,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1201:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":106,"nodeType":"FunctionDefinition","src":"1259:235:0","nodes":[],"body":{"id":105,"nodeType":"Block","src":"1432:62:0","nodes":[],"statements":[{"AST":{"nodeType":"YulBlock","src":"1451:37:0","statements":[{"nodeType":"YulAssignment","src":"1465:13:0","value":{"name":"fnIn","nodeType":"YulIdentifier","src":"1474:4:0"},"variableNames":[{"name":"fnOut","nodeType":"YulIdentifier","src":"1465:5:0"}]}]},"evmVersion":"london","externalReferences":[{"declaration":95,"isOffset":false,"isSlot":false,"src":"1474:4:0","valueSize":1},{"declaration":102,"isOffset":false,"isSlot":false,"src":"1465:5:0","valueSize":1}],"id":104,"nodeType":"InlineAssembly","src":"1442:46:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_castLogPayloadViewToPure","nameLocation":"1268:25:0","parameters":{"id":96,"nodeType":"ParameterList","parameters":[{"constant":false,"id":95,"mutability":"mutable","name":"fnIn","nameLocation":"1331:4:0","nodeType":"VariableDeclaration","scope":106,"src":"1294:41:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"typeName":{"id":94,"nodeType":"FunctionTypeName","parameterTypes":{"id":92,"nodeType":"ParameterList","parameters":[{"constant":false,"id":91,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":94,"src":"1303:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":90,"name":"bytes","nodeType":"ElementaryTypeName","src":"1303:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1302:14:0"},"returnParameterTypes":{"id":93,"nodeType":"ParameterList","parameters":[],"src":"1331:0:0"},"src":"1294:41:0","stateMutability":"view","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) view"},"visibility":"internal"},"visibility":"internal"}],"src":"1293:43:0"},"returnParameters":{"id":103,"nodeType":"ParameterList","parameters":[{"constant":false,"id":102,"mutability":"mutable","name":"fnOut","nameLocation":"1421:5:0","nodeType":"VariableDeclaration","scope":106,"src":"1384:42:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"typeName":{"id":101,"nodeType":"FunctionTypeName","parameterTypes":{"id":99,"nodeType":"ParameterList","parameters":[{"constant":false,"id":98,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":101,"src":"1393:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":97,"name":"bytes","nodeType":"ElementaryTypeName","src":"1393:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1392:14:0"},"returnParameterTypes":{"id":100,"nodeType":"ParameterList","parameters":[],"src":"1421:0:0"},"src":"1384:42:0","stateMutability":"pure","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes) pure"},"visibility":"internal"},"visibility":"internal"}],"src":"1383:44:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":118,"nodeType":"FunctionDefinition","src":"1500:133:0","nodes":[],"body":{"id":117,"nodeType":"Block","src":"1561:72:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":114,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":108,"src":"1618:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"arguments":[{"id":112,"name":"_sendLogPayloadView","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":134,"src":"1597:19:0","typeDescriptions":{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_function_internal_view$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) view"}],"id":111,"name":"_castLogPayloadViewToPure","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":106,"src":"1571:25:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_function_internal_view$_t_bytes_memory_ptr_$returns$__$_$returns$_t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$_$","typeString":"function (function (bytes memory) view) pure returns (function (bytes memory) pure)"}},"id":113,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":115,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"1571:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":116,"nodeType":"ExpressionStatement","src":"1571:55:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayload","nameLocation":"1509:15:0","parameters":{"id":109,"nodeType":"ParameterList","parameters":[{"constant":false,"id":108,"mutability":"mutable","name":"payload","nameLocation":"1538:7:0","nodeType":"VariableDeclaration","scope":118,"src":"1525:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":107,"name":"bytes","nodeType":"ElementaryTypeName","src":"1525:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1524:22:0"},"returnParameters":{"id":110,"nodeType":"ParameterList","parameters":[],"src":"1561:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":134,"nodeType":"FunctionDefinition","src":"1639:380:0","nodes":[],"body":{"id":133,"nodeType":"Block","src":"1703:316:0","nodes":[],"statements":[{"assignments":[124],"declarations":[{"constant":false,"id":124,"mutability":"mutable","name":"payloadLength","nameLocation":"1721:13:0","nodeType":"VariableDeclaration","scope":133,"src":"1713:21:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":123,"name":"uint256","nodeType":"ElementaryTypeName","src":"1713:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":127,"initialValue":{"expression":{"id":125,"name":"payload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":120,"src":"1737:7:0","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}},"id":126,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"length","nodeType":"MemberAccess","src":"1737:14:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"1713:38:0"},{"assignments":[129],"declarations":[{"constant":false,"id":129,"mutability":"mutable","name":"consoleAddress","nameLocation":"1769:14:0","nodeType":"VariableDeclaration","scope":133,"src":"1761:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":128,"name":"address","nodeType":"ElementaryTypeName","src":"1761:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":131,"initialValue":{"id":130,"name":"CONSOLE_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":89,"src":"1786:15:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"1761:40:0"},{"AST":{"nodeType":"YulBlock","src":"1863:150:0","statements":[{"nodeType":"YulVariableDeclaration","src":"1877:36:0","value":{"arguments":[{"name":"payload","nodeType":"YulIdentifier","src":"1901:7:0"},{"kind":"number","nodeType":"YulLiteral","src":"1910:2:0","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1897:3:0"},"nodeType":"YulFunctionCall","src":"1897:16:0"},"variables":[{"name":"payloadStart","nodeType":"YulTypedName","src":"1881:12:0","type":""}]},{"nodeType":"YulVariableDeclaration","src":"1926:77:0","value":{"arguments":[{"arguments":[],"functionName":{"name":"gas","nodeType":"YulIdentifier","src":"1946:3:0"},"nodeType":"YulFunctionCall","src":"1946:5:0"},{"name":"consoleAddress","nodeType":"YulIdentifier","src":"1953:14:0"},{"name":"payloadStart","nodeType":"YulIdentifier","src":"1969:12:0"},{"name":"payloadLength","nodeType":"YulIdentifier","src":"1983:13:0"},{"kind":"number","nodeType":"YulLiteral","src":"1998:1:0","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2001:1:0","type":"","value":"0"}],"functionName":{"name":"staticcall","nodeType":"YulIdentifier","src":"1935:10:0"},"nodeType":"YulFunctionCall","src":"1935:68:0"},"variables":[{"name":"r","nodeType":"YulTypedName","src":"1930:1:0","type":""}]}]},"documentation":"@solidity memory-safe-assembly","evmVersion":"london","externalReferences":[{"declaration":129,"isOffset":false,"isSlot":false,"src":"1953:14:0","valueSize":1},{"declaration":120,"isOffset":false,"isSlot":false,"src":"1901:7:0","valueSize":1},{"declaration":124,"isOffset":false,"isSlot":false,"src":"1983:13:0","valueSize":1}],"id":132,"nodeType":"InlineAssembly","src":"1854:159:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"_sendLogPayloadView","nameLocation":"1648:19:0","parameters":{"id":121,"nodeType":"ParameterList","parameters":[{"constant":false,"id":120,"mutability":"mutable","name":"payload","nameLocation":"1681:7:0","nodeType":"VariableDeclaration","scope":134,"src":"1668:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":119,"name":"bytes","nodeType":"ElementaryTypeName","src":"1668:5:0","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"1667:22:0"},"returnParameters":{"id":122,"nodeType":"ParameterList","parameters":[],"src":"1703:0:0"},"scope":220,"stateMutability":"view","virtual":false,"visibility":"private"},{"id":148,"nodeType":"FunctionDefinition","src":"2025:164:0","nodes":[],"body":{"id":147,"nodeType":"Block","src":"2070:119:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e6729","id":142,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2120:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},"value":"log(string)"},{"id":143,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":136,"src":"2135:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50","typeString":"literal_string \"log(string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":140,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2096:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":141,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2096:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":144,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2096:42:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":139,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2080:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":145,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2080:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":146,"nodeType":"ExpressionStatement","src":"2080:59:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2034:3:0","parameters":{"id":137,"nodeType":"ParameterList","parameters":[{"constant":false,"id":136,"mutability":"mutable","name":"p0","nameLocation":"2052:2:0","nodeType":"VariableDeclaration","scope":148,"src":"2038:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":135,"name":"string","nodeType":"ElementaryTypeName","src":"2038:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2037:18:0"},"returnParameters":{"id":138,"nodeType":"ParameterList","parameters":[],"src":"2070:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":165,"nodeType":"FunctionDefinition","src":"2195:182:0","nodes":[],"body":{"id":164,"nodeType":"Block","src":"2249:128:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c626f6f6c29","id":158,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2299:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},"value":"log(string,bool)"},{"id":159,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":150,"src":"2319:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":160,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":152,"src":"2323:2:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_c3b556354c088fbb43886eb83c2a04bc7089663f964d22be308197a236f5b870","typeString":"literal_string \"log(string,bool)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":156,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2275:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":157,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2275:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":161,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2275:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":155,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2259:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":162,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2259:68:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":163,"nodeType":"ExpressionStatement","src":"2259:68:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2204:3:0","parameters":{"id":153,"nodeType":"ParameterList","parameters":[{"constant":false,"id":150,"mutability":"mutable","name":"p0","nameLocation":"2222:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2208:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":149,"name":"string","nodeType":"ElementaryTypeName","src":"2208:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":152,"mutability":"mutable","name":"p1","nameLocation":"2231:2:0","nodeType":"VariableDeclaration","scope":165,"src":"2226:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":151,"name":"bool","nodeType":"ElementaryTypeName","src":"2226:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"2207:27:0"},"returnParameters":{"id":154,"nodeType":"ParameterList","parameters":[],"src":"2249:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":182,"nodeType":"FunctionDefinition","src":"2383:188:0","nodes":[],"body":{"id":181,"nodeType":"Block","src":"2440:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c75696e7432353629","id":175,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2490:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},"value":"log(string,uint256)"},{"id":176,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":167,"src":"2513:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":177,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":169,"src":"2517:2:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b60e72ccf6d57ab53eb84d7e94a9545806ed7f93c4d5673f11a64f03471e584e","typeString":"literal_string \"log(string,uint256)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":173,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2466:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":174,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2466:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":178,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2466:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":172,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2450:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":179,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2450:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":180,"nodeType":"ExpressionStatement","src":"2450:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2392:3:0","parameters":{"id":170,"nodeType":"ParameterList","parameters":[{"constant":false,"id":167,"mutability":"mutable","name":"p0","nameLocation":"2410:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2396:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":166,"name":"string","nodeType":"ElementaryTypeName","src":"2396:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":169,"mutability":"mutable","name":"p1","nameLocation":"2422:2:0","nodeType":"VariableDeclaration","scope":182,"src":"2414:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":168,"name":"uint256","nodeType":"ElementaryTypeName","src":"2414:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"2395:30:0"},"returnParameters":{"id":171,"nodeType":"ParameterList","parameters":[],"src":"2440:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":199,"nodeType":"FunctionDefinition","src":"2577:188:0","nodes":[],"body":{"id":198,"nodeType":"Block","src":"2634:131:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c6164647265737329","id":192,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2684:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},"value":"log(string,address)"},{"id":193,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":184,"src":"2707:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":194,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":186,"src":"2711:2:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_319af333460570a1937bf195dd33445c0d0951c59127da6f1f038b9fdce3fd72","typeString":"literal_string \"log(string,address)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":190,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2660:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":191,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2660:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":195,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2660:54:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":189,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2644:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":196,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2644:71:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":197,"nodeType":"ExpressionStatement","src":"2644:71:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2586:3:0","parameters":{"id":187,"nodeType":"ParameterList","parameters":[{"constant":false,"id":184,"mutability":"mutable","name":"p0","nameLocation":"2604:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2590:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":183,"name":"string","nodeType":"ElementaryTypeName","src":"2590:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":186,"mutability":"mutable","name":"p1","nameLocation":"2616:2:0","nodeType":"VariableDeclaration","scope":199,"src":"2608:10:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":185,"name":"address","nodeType":"ElementaryTypeName","src":"2608:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"2589:30:0"},"returnParameters":{"id":188,"nodeType":"ParameterList","parameters":[],"src":"2634:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"},{"id":219,"nodeType":"FunctionDefinition","src":"2771:222:0","nodes":[],"body":{"id":218,"nodeType":"Block","src":"2852:141:0","nodes":[],"statements":[{"expression":{"arguments":[{"arguments":[{"hexValue":"6c6f6728737472696e672c737472696e672c737472696e6729","id":211,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"2902:27:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},"value":"log(string,string,string)"},{"id":212,"name":"p0","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":201,"src":"2931:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":213,"name":"p1","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":203,"src":"2935:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"id":214,"name":"p2","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":205,"src":"2939:2:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2ced7cef693312206c21f0e92e3b54e2e16bf33db5eec350c78866822c665e1f","typeString":"literal_string \"log(string,string,string)\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":209,"name":"abi","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-1,"src":"2878:3:0","typeDescriptions":{"typeIdentifier":"t_magic_abi","typeString":"abi"}},"id":210,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"memberName":"encodeWithSignature","nodeType":"MemberAccess","src":"2878:23:0","typeDescriptions":{"typeIdentifier":"t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) pure returns (bytes memory)"}},"id":215,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2878:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"id":208,"name":"_sendLogPayload","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":118,"src":"2862:15:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory) pure"}},"id":216,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"2862:81:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":217,"nodeType":"ExpressionStatement","src":"2862:81:0"}]},"implemented":true,"kind":"function","modifiers":[],"name":"log","nameLocation":"2780:3:0","parameters":{"id":206,"nodeType":"ParameterList","parameters":[{"constant":false,"id":201,"mutability":"mutable","name":"p0","nameLocation":"2798:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2784:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":200,"name":"string","nodeType":"ElementaryTypeName","src":"2784:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":203,"mutability":"mutable","name":"p1","nameLocation":"2816:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2802:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":202,"name":"string","nodeType":"ElementaryTypeName","src":"2802:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"},{"constant":false,"id":205,"mutability":"mutable","name":"p2","nameLocation":"2834:2:0","nodeType":"VariableDeclaration","scope":219,"src":"2820:16:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":204,"name":"string","nodeType":"ElementaryTypeName","src":"2820:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"2783:54:0"},"returnParameters":{"id":207,"nodeType":"ParameterList","parameters":[],"src":"2852:0:0"},"scope":220,"stateMutability":"pure","virtual":false,"visibility":"internal"}],"abstract":false,"baseContracts":[],"canonicalName":"console","contractDependencies":[],"contractKind":"library","fullyImplemented":true,"linearizedBaseContracts":[220],"name":"console","nameLocation":"1152:7:0","scope":969,"usedErrors":[]},{"id":786,"nodeType":"ContractDefinition","src":"3123:3912:0","nodes":[{"id":235,"nodeType":"VariableDeclaration","src":"3152:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"3178:10:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":222,"name":"address","nodeType":"ElementaryTypeName","src":"3152:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":230,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3225:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":229,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"3215:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":231,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3215:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":228,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3207:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":227,"name":"uint256","nodeType":"ElementaryTypeName","src":"3207:7:0","typeDescriptions":{}}},"id":232,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3207:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":226,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3199:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":225,"name":"uint160","nodeType":"ElementaryTypeName","src":"3199:7:0","typeDescriptions":{}}},"id":233,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3199:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":224,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3191:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":223,"name":"address","nodeType":"ElementaryTypeName","src":"3191:7:0","typeDescriptions":{}}},"id":234,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3191:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":241,"nodeType":"VariableDeclaration","src":"3252:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"3273:2:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":237,"nodeType":"UserDefinedTypeName","pathNode":{"id":236,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"3252:2:0"},"referencedDeclaration":83,"src":"3252:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":239,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":235,"src":"3281:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":238,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"3278:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":240,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3278:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":243,"nodeType":"VariableDeclaration","src":"3356:22:0","nodes":[],"constant":false,"functionSelector":"61bc221a","mutability":"mutable","name":"counter","nameLocation":"3371:7:0","scope":786,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":242,"name":"uint256","nodeType":"ElementaryTypeName","src":"3356:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":456,"nodeType":"FunctionDefinition","src":"3468:1428:0","nodes":[],"body":{"id":455,"nodeType":"Block","src":"3490:1406:0","nodes":[],"statements":[{"assignments":[248],"declarations":[{"constant":false,"id":248,"mutability":"mutable","name":"x","nameLocation":"3505:1:0","nodeType":"VariableDeclaration","scope":455,"src":"3500:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":247,"name":"bool","nodeType":"ElementaryTypeName","src":"3500:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"id":254,"initialValue":{"arguments":[{"hexValue":"4558414d504c455f424f4f4c","id":251,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3518:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},"value":"EXAMPLE_BOOL"},{"hexValue":"66616c7365","id":252,"isConstant":false,"isLValue":false,"isPure":true,"kind":"bool","lValueRequested":false,"nodeType":"Literal","src":"3534:5:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"value":"false"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a634dae177a0e138ae7aaa2afae347412e148992e88c7aabd33ee71be146cb7f","typeString":"literal_string \"EXAMPLE_BOOL\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":249,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3509:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":250,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"envOr","nodeType":"MemberAccess","referencedDeclaration":10,"src":"3509:8:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$_t_bool_$returns$_t_bool_$","typeString":"function (string memory,bool) view external returns (bool)"}},"id":253,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3509:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"nodeType":"VariableDeclarationStatement","src":"3500:40:0"},{"expression":{"arguments":[{"hexValue":"626f6f6c2076616c75652066726f6d20656e76","id":258,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3562:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},"value":"bool value from env"},{"id":259,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":248,"src":"3585:1:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5a607d0b5a1295325aa8901721d78ba402601bba6f62cebdd5235dd0204a590b","typeString":"literal_string \"bool value from env\""},{"typeIdentifier":"t_bool","typeString":"bool"}],"expression":{"id":255,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3550:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":257,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":165,"src":"3550:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_bool_$returns$__$","typeString":"function (string memory,bool) pure"}},"id":260,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3550:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":261,"nodeType":"ExpressionStatement","src":"3550:37:0"},{"expression":{"arguments":[{"hexValue":"636f6e74726163742061646472","id":265,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3610:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},"value":"contract addr"},{"arguments":[{"id":268,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3635:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":267,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3627:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":266,"name":"address","nodeType":"ElementaryTypeName","src":"3627:7:0","typeDescriptions":{}}},"id":269,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3627:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa50728770d00fe8f6a0592f3565bbfaf063ee4077f1f5bbc003d091d33cd0c4","typeString":"literal_string \"contract addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":262,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3598:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":264,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3598:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":270,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3598:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":271,"nodeType":"ExpressionStatement","src":"3598:43:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206e6f6e6365","id":275,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3663:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},"value":"contract nonce"},{"arguments":[{"arguments":[{"id":280,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"3701:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":279,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3693:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":278,"name":"address","nodeType":"ElementaryTypeName","src":"3693:7:0","typeDescriptions":{}}},"id":281,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3693:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":276,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3681:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":277,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3681:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":282,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3681:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_3a23091615a5de8c0a35ffd8857a37e2c4e0b72f3ef8a34d6caf65efcd562e2f","typeString":"literal_string \"contract nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":272,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3651:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":274,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3651:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":283,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3651:57:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":284,"nodeType":"ExpressionStatement","src":"3651:57:0"},{"expression":{"arguments":[{"hexValue":"73656e6465722061646472","id":288,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3730:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},"value":"sender addr"},{"arguments":[{"expression":{"id":291,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3753:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":292,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3753:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":290,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3745:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":289,"name":"address","nodeType":"ElementaryTypeName","src":"3745:7:0","typeDescriptions":{}}},"id":293,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3745:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8125ca2decf812b25b65606ff16dad37cb198ff0433485a7926e50feafacfc35","typeString":"literal_string \"sender addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":285,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3718:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":287,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"3718:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":294,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3718:47:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":295,"nodeType":"ExpressionStatement","src":"3718:47:0"},{"expression":{"arguments":[{"hexValue":"73656e646572206e6f6e6365","id":299,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3787:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},"value":"sender nonce"},{"arguments":[{"arguments":[{"expression":{"id":304,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"3823:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":305,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"3823:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":303,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"3815:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":302,"name":"address","nodeType":"ElementaryTypeName","src":"3815:7:0","typeDescriptions":{}}},"id":306,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3815:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":300,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3803:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":301,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"3803:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":307,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3803:32:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_db7deb43f2f9e0404016de53b7e64c4976b54149581f7534daae2551e8cf4e40","typeString":"literal_string \"sender nonce\""},{"typeIdentifier":"t_uint64","typeString":"uint64"}],"expression":{"id":296,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3775:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":298,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"3775:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":308,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3775:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":309,"nodeType":"ExpressionStatement","src":"3775:61:0"},{"assignments":[311],"declarations":[{"constant":false,"id":311,"mutability":"mutable","name":"json","nameLocation":"3861:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3847:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":310,"name":"string","nodeType":"ElementaryTypeName","src":"3847:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"id":313,"initialValue":{"hexValue":"7b22726f6f745f6b6579223a205b7b2261223a20312c202262223a20327d5d7d","id":312,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3868:34:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_e95522e99766888d0261f55bd1eae5e3f3e26eaf009a16e2433eafaf0a4ecdf2","typeString":"literal_string \"{\"root_key\": [{\"a\": 1, \"b\": 2}]}\""},"value":"{\"root_key\": [{\"a\": 1, \"b\": 2}]}"},"nodeType":"VariableDeclarationStatement","src":"3847:55:0"},{"assignments":[318],"declarations":[{"constant":false,"id":318,"mutability":"mutable","name":"keys","nameLocation":"3928:4:0","nodeType":"VariableDeclaration","scope":455,"src":"3912:20:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string[]"},"typeName":{"baseType":{"id":316,"name":"string","nodeType":"ElementaryTypeName","src":"3912:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"id":317,"nodeType":"ArrayTypeName","src":"3912:8:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_storage_$dyn_storage_ptr","typeString":"string[]"}},"visibility":"internal"}],"id":324,"initialValue":{"arguments":[{"id":321,"name":"json","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":311,"src":"3952:4:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"hexValue":"2e726f6f745f6b65795b305d","id":322,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3958:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""},"value":".root_key[0]"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_stringliteral_d82f67100edb80050915e1ec4b565c9a8319a22efb1075e1298b7bb60101d266","typeString":"literal_string \".root_key[0]\""}],"expression":{"id":319,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"3935:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":320,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"parseJsonKeys","nodeType":"MemberAccess","referencedDeclaration":27,"src":"3935:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$_t_array$_t_string_memory_ptr_$dyn_memory_ptr_$","typeString":"function (string memory,string memory) pure external returns (string memory[] memory)"}},"id":323,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3935:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"nodeType":"VariableDeclarationStatement","src":"3912:61:0"},{"expression":{"arguments":[{"hexValue":"6b657973","id":328,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"3995:6:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},"value":"keys"},{"baseExpression":{"id":329,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4003:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":331,"indexExpression":{"hexValue":"30","id":330,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4008:1:0","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4003:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},{"baseExpression":{"id":332,"name":"keys","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":318,"src":"4012:4:0","typeDescriptions":{"typeIdentifier":"t_array$_t_string_memory_ptr_$dyn_memory_ptr","typeString":"string memory[] memory"}},"id":334,"indexExpression":{"hexValue":"31","id":333,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4017:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"isConstant":false,"isLValue":true,"isPure":false,"lValueRequested":false,"nodeType":"IndexAccess","src":"4012:7:0","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f29790a80c4ce5f42f59892f424f9c92856c6b656c3378e2cf305b260c6f4195","typeString":"literal_string \"keys\""},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"},{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}],"expression":{"id":325,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"3983:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":327,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":219,"src":"3983:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_string_memory_ptr_$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory,string memory,string memory) pure"}},"id":335,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"3983:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":336,"nodeType":"ExpressionStatement","src":"3983:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c","id":340,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4042:15:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""},"value":"from original"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_77928970c8757d110f3c23e003246f49e0de890480ba9717ba659b2f56f316b2","typeString":"literal_string \"from original\""}],"expression":{"id":337,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4031:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":339,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4031:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":341,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4031:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":342,"nodeType":"ExpressionStatement","src":"4031:27:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"30783432","id":350,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"4098:4:0","typeDescriptions":{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"},"value":"0x42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_66_by_1","typeString":"int_const 66"}],"id":349,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4090:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":348,"name":"uint160","nodeType":"ElementaryTypeName","src":"4090:7:0","typeDescriptions":{}}},"id":351,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4090:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":347,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4082:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":346,"name":"address","nodeType":"ElementaryTypeName","src":"4082:7:0","typeDescriptions":{}}},"id":352,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4082:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":343,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4068:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":345,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startPrank","nodeType":"MemberAccess","referencedDeclaration":32,"src":"4068:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":353,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4068:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":354,"nodeType":"ExpressionStatement","src":"4068:37:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2031","id":358,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4126:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""},"value":"from prank 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_42b34abfe37a8b0add910cda7b4a379e6538fa7a1dcafce47a02bd38f6c88e2a","typeString":"literal_string \"from prank 1\""}],"expression":{"id":355,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4115:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":357,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4115:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":359,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4115:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":360,"nodeType":"ExpressionStatement","src":"4115:26:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f7065206d73672e73656e646572","id":364,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4163:25:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},"value":"parent scope msg.sender"},{"arguments":[{"expression":{"id":367,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"4198:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":368,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"4198:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":366,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4190:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":365,"name":"address","nodeType":"ElementaryTypeName","src":"4190:7:0","typeDescriptions":{}}},"id":369,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4190:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_83ec9246154d8845de47aafc5c2865c9985d2efe84472c27283879f2fbf5cc94","typeString":"literal_string \"parent scope msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":361,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4151:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":363,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4151:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":370,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4151:59:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":371,"nodeType":"ExpressionStatement","src":"4151:59:0"},{"expression":{"arguments":[{"hexValue":"706172656e742073636f706520636f6e74726163742e61646472","id":375,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4232:28:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},"value":"parent scope contract.addr"},{"arguments":[{"id":378,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4270:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":377,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4262:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":376,"name":"address","nodeType":"ElementaryTypeName","src":"4262:7:0","typeDescriptions":{}}},"id":379,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4262:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_97df66250e0b2b48f0ec8d0e01eb1b8ca012d95f1572895622aa1ea433e5570f","typeString":"literal_string \"parent scope contract.addr\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":372,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4220:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":374,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"4220:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":380,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4220:56:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":381,"nodeType":"ExpressionStatement","src":"4220:56:0"},{"expression":{"arguments":[{"hexValue":"66726f6d207072616e6b2032","id":385,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4297:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""},"value":"from prank 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_a38a34f8cad750a79aa097a92971f8f405b51ee9d53d25c5b14fc129ba3684bb","typeString":"literal_string \"from prank 2\""}],"expression":{"id":382,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4286:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":384,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4286:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":386,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4286:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":387,"nodeType":"ExpressionStatement","src":"4286:26:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":388,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4322:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":390,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopPrank","nodeType":"MemberAccess","referencedDeclaration":35,"src":"4322:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":391,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4322:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":392,"nodeType":"ExpressionStatement","src":"4322:14:0"},{"expression":{"arguments":[{"hexValue":"66726f6d206f726967696e616c20616761696e","id":396,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4357:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""},"value":"from original again"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_0c805c6579e20a9c4c8e11aeab23330910a9f2da629191dc119d1730e8ed6860","typeString":"literal_string \"from original again\""}],"expression":{"id":393,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4346:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":395,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"hello","nodeType":"MemberAccess","referencedDeclaration":713,"src":"4346:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) view external"}},"id":397,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4346:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":398,"nodeType":"ExpressionStatement","src":"4346:33:0"},{"assignments":[400],"declarations":[{"constant":false,"id":400,"mutability":"mutable","name":"tmpNonceGetter","nameLocation":"4480:14:0","nodeType":"VariableDeclaration","scope":455,"src":"4472:22:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":399,"name":"address","nodeType":"ElementaryTypeName","src":"4472:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":413,"initialValue":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"74656d70206e6f6e6365207465737420676574746572","id":408,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4531:24:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""},"value":"temp nonce test getter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_12520bf22cf2eb7252f13fda2b7eb7ddaed1b3456e20c8008c714c7ba4d9a252","typeString":"literal_string \"temp nonce test getter\""}],"id":407,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"4521:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":409,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4521:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":406,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4513:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":405,"name":"uint256","nodeType":"ElementaryTypeName","src":"4513:7:0","typeDescriptions":{}}},"id":410,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4513:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":404,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4505:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":403,"name":"uint160","nodeType":"ElementaryTypeName","src":"4505:7:0","typeDescriptions":{}}},"id":411,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4505:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":402,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4497:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":401,"name":"address","nodeType":"ElementaryTypeName","src":"4497:7:0","typeDescriptions":{}}},"id":412,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4497:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"4472:87:0"},{"expression":{"arguments":[{"id":417,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4577:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},{"arguments":[{"hexValue":"5363726970744578616d706c652e732e736f6c3a4e6f6e6365476574746572","id":420,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4612:33:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""},"value":"ScriptExample.s.sol:NonceGetter"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_6ff7ab2e79e6b7d182bbfccfe7f8e2118d655ff1b4bf1a4f4ed2eab0f3f8c825","typeString":"literal_string \"ScriptExample.s.sol:NonceGetter\""}],"expression":{"id":418,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4593:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":419,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getDeployedCode","nodeType":"MemberAccess","referencedDeclaration":61,"src":"4593:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$","typeString":"function (string memory) view external returns (bytes memory)"}},"id":421,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4593:53:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"},{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes memory"}],"expression":{"id":414,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4569:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":416,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"etch","nodeType":"MemberAccess","referencedDeclaration":68,"src":"4569:7:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$_t_bytes_memory_ptr_$returns$__$","typeString":"function (address,bytes memory) external"}},"id":422,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4569:78:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":423,"nodeType":"ExpressionStatement","src":"4569:78:0"},{"expression":{"arguments":[{"id":427,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4676:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":424,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"4657:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":426,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"allowCheatcodes","nodeType":"MemberAccess","referencedDeclaration":73,"src":"4657:18:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":428,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4657:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":429,"nodeType":"ExpressionStatement","src":"4657:34:0"},{"assignments":[431],"declarations":[{"constant":false,"id":431,"mutability":"mutable","name":"v","nameLocation":"4709:1:0","nodeType":"VariableDeclaration","scope":455,"src":"4701:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":430,"name":"uint256","nodeType":"ElementaryTypeName","src":"4701:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":441,"initialValue":{"arguments":[{"arguments":[{"id":438,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"4758:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":437,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"4750:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":436,"name":"address","nodeType":"ElementaryTypeName","src":"4750:7:0","typeDescriptions":{}}},"id":439,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4750:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"arguments":[{"id":433,"name":"tmpNonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":400,"src":"4725:14:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":432,"name":"NonceGetter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":833,"src":"4713:11:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_NonceGetter_$833_$","typeString":"type(contract NonceGetter)"}},"id":434,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:27:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_NonceGetter_$833","typeString":"contract NonceGetter"}},"id":435,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":832,"src":"4713:36:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint256_$","typeString":"function (address) view external returns (uint256)"}},"id":440,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4713:51:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"VariableDeclarationStatement","src":"4701:63:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e63652066726f6d206e6f6e6365206765747465722c206e6f206578706c6963697420616363657373207265717569726564207769746820766d2e657463683a","id":445,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4786:68:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},"value":"nonce from nonce getter, no explicit access required with vm.etch:"},{"id":446,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":431,"src":"4856:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_afafcfffb72f22a98864f79a750e1a4a41d7dd81365e873e06ff57a1a9f42b11","typeString":"literal_string \"nonce from nonce getter, no explicit access required with vm.etch:\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":442,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4774:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":444,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"4774:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":447,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4774:84:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":448,"nodeType":"ExpressionStatement","src":"4774:84:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":452,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"4881:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":449,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"4869:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":451,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"4869:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":453,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"4869:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":454,"nodeType":"ExpressionStatement","src":"4869:20:0"}]},"documentation":{"id":244,"nodeType":"StructuredDocumentation","src":"3385:78:0","text":"@notice example function, runs through basic cheat-codes and console logs."},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"3477:3:0","parameters":{"id":245,"nodeType":"ParameterList","parameters":[],"src":"3480:2:0"},"returnParameters":{"id":246,"nodeType":"ParameterList","parameters":[],"src":"3490:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":689,"nodeType":"FunctionDefinition","src":"4963:1333:0","nodes":[],"body":{"id":688,"nodeType":"Block","src":"4994:1302:0","nodes":[],"statements":[{"expression":{"arguments":[{"hexValue":"6e6f6e6365207374617274","id":463,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5016:13:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},"value":"nonce start"},{"arguments":[{"arguments":[{"arguments":[{"id":470,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5059:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":469,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5051:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":468,"name":"address","nodeType":"ElementaryTypeName","src":"5051:7:0","typeDescriptions":{}}},"id":471,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5051:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":466,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5039:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":467,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"5039:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":472,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5039:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":465,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5031:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":464,"name":"uint256","nodeType":"ElementaryTypeName","src":"5031:7:0","typeDescriptions":{}}},"id":473,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5031:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_71efc69b9a13b6bc1e9a14d766ff01c79022262c6daa6532fb5dfb14f8511a20","typeString":"literal_string \"nonce start\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":460,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5004:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":462,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"5004:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":474,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5004:63:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":475,"nodeType":"ExpressionStatement","src":"5004:63:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073696e676c65","id":479,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5090:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""},"value":"testing single"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b75103528423218e7569082dad569ed0d2ce7c0ac770c0812b220e2d369fe474","typeString":"literal_string \"testing single\""}],"expression":{"id":476,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5078:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":478,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5078:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":480,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5078:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":481,"nodeType":"ExpressionStatement","src":"5078:29:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":482,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5117:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":484,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"5117:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":485,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5117:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":486,"nodeType":"ExpressionStatement","src":"5117:14:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c31","id":490,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5152:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""},"value":"single_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_5e1cad6d7a968cfacf2731373e1248ffb11f4886bced66a02a6de1a67ac8f777","typeString":"literal_string \"single_call1\""}],"expression":{"id":487,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5141:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":489,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5141:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":491,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5141:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":492,"nodeType":"ExpressionStatement","src":"5141:26:0"},{"expression":{"arguments":[{"hexValue":"73696e676c655f63616c6c32","id":496,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5188:14:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""},"value":"single_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b37ddaf5d00ad9e6371de3fb71b91eef731fae1e86b768666380f7d44e1ada25","typeString":"literal_string \"single_call2\""}],"expression":{"id":493,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5177:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":495,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5177:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":497,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5177:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":498,"nodeType":"ExpressionStatement","src":"5177:26:0"},{"expression":{"arguments":[{"hexValue":"74657374696e672073746172742f73746f70","id":502,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5226:20:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""},"value":"testing start/stop"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_778e886e3a1c3c5096aca76228832105f3f9269f362effd0e8ce3737787cb784","typeString":"literal_string \"testing start/stop\""}],"expression":{"id":499,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5214:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":501,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5214:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":503,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5214:33:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":504,"nodeType":"ExpressionStatement","src":"5214:33:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078633066666565","id":512,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5291:8:0","typeDescriptions":{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"},"value":"0xc0ffee"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_12648430_by_1","typeString":"int_const 12648430"}],"id":511,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5283:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":510,"name":"uint160","nodeType":"ElementaryTypeName","src":"5283:7:0","typeDescriptions":{}}},"id":513,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5283:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":509,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5275:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":508,"name":"address","nodeType":"ElementaryTypeName","src":"5275:7:0","typeDescriptions":{}}},"id":514,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5275:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":505,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5257:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":507,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5257:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":515,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5257:45:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":516,"nodeType":"ExpressionStatement","src":"5257:45:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c31","id":520,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5323:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""},"value":"startstop_call1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_2fc2682edf10ed478ee3b9a190f6b1c88bb492b300935ce44545a1613cf8f041","typeString":"literal_string \"startstop_call1\""}],"expression":{"id":517,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5312:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":519,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5312:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":521,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5312:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":522,"nodeType":"ExpressionStatement","src":"5312:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c32","id":526,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5362:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""},"value":"startstop_call2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_1a6fd77f04b28bf45d6d0e2dd4c65c0bbfeba174f849e43bb67ebca1c019cda4","typeString":"literal_string \"startstop_call2\""}],"expression":{"id":523,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5351:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":525,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call2","nodeType":"MemberAccess","referencedDeclaration":743,"src":"5351:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":527,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5351:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":528,"nodeType":"ExpressionStatement","src":"5351:29:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f70757265","id":532,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5404:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""},"value":"startstop_pure"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6e9eb1efd186b1d92b54da45026aa97a178e6eaffdf9dbf9f666fc751fb0ff9","typeString":"literal_string \"startstop_pure\""}],"expression":{"id":529,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5390:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":531,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"callPure","nodeType":"MemberAccess","referencedDeclaration":785,"src":"5390:13:0","typeDescriptions":{"typeIdentifier":"t_function_external_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure external"}},"id":533,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5390:31:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":534,"nodeType":"ExpressionStatement","src":"5390:31:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":535,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5431:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":537,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5431:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":538,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5431:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":539,"nodeType":"ExpressionStatement","src":"5431:18:0"},{"expression":{"arguments":[{"hexValue":"737461727473746f705f63616c6c33","id":543,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5470:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""},"value":"startstop_call3"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_8eb502bfdc4adda22bd960aa2ae13ce4c0ed8cc3b3791ed65e321a38cdd36f72","typeString":"literal_string \"startstop_call3\""}],"expression":{"id":540,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5459:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":542,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"call1","nodeType":"MemberAccess","referencedDeclaration":728,"src":"5459:10:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":544,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5459:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":545,"nodeType":"ExpressionStatement","src":"5459:29:0"},{"expression":{"arguments":[{"hexValue":"74657374696e67206e6573746564","id":549,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5511:16:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""},"value":"testing nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_f92f19f7a5b5b9ce341188bf4e15925f184cdb5ac135c4846ced718f259dbde5","typeString":"literal_string \"testing nested\""}],"expression":{"id":546,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5499:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":548,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5499:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":550,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5499:29:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":551,"nodeType":"ExpressionStatement","src":"5499:29:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307831323334","id":559,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5572:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":558,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5564:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":557,"name":"uint160","nodeType":"ElementaryTypeName","src":"5564:7:0","typeDescriptions":{}}},"id":560,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5564:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":556,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5556:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":555,"name":"address","nodeType":"ElementaryTypeName","src":"5556:7:0","typeDescriptions":{}}},"id":561,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5556:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":552,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5538:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":554,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"startBroadcast","nodeType":"MemberAccess","referencedDeclaration":48,"src":"5538:17:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":562,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5538:43:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":563,"nodeType":"ExpressionStatement","src":"5538:43:0"},{"expression":{"arguments":[{"hexValue":"6e6573746564","id":567,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5604:8:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""},"value":"nested"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4d5b14044d78fbf0c9dd8b9c49e35f09ee5a6f5b1b3b8117b5d0e15c8dd2cb09","typeString":"literal_string \"nested\""}],"expression":{"id":564,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"5591:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":566,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested1","nodeType":"MemberAccess","referencedDeclaration":758,"src":"5591:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":568,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5591:22:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":569,"nodeType":"ExpressionStatement","src":"5591:22:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":570,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5623:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":572,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"stopBroadcast","nodeType":"MemberAccess","referencedDeclaration":54,"src":"5623:16:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":573,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5623:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":574,"nodeType":"ExpressionStatement","src":"5623:18:0"},{"expression":{"arguments":[{"hexValue":"636f6e7472616374206465706c6f796d656e74","id":578,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5664:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""},"value":"contract deployment"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_aaf9be86adf9b6872d87eed3526f7c55f3c5d61f4e4dd6d55ef2fcbb8ad0bd57","typeString":"literal_string \"contract deployment\""}],"expression":{"id":575,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5652:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":577,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5652:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":579,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5652:34:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":580,"nodeType":"ExpressionStatement","src":"5652:34:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"3078313233343536","id":588,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5725:8:0","typeDescriptions":{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"},"value":"0x123456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1193046_by_1","typeString":"int_const 1193046"}],"id":587,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5717:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":586,"name":"uint160","nodeType":"ElementaryTypeName","src":"5717:7:0","typeDescriptions":{}}},"id":589,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5717:17:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":585,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5709:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":584,"name":"address","nodeType":"ElementaryTypeName","src":"5709:7:0","typeDescriptions":{}}},"id":590,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5709:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":581,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5696:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":583,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5696:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":591,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5696:40:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":592,"nodeType":"ExpressionStatement","src":"5696:40:0"},{"assignments":[595],"declarations":[{"constant":false,"id":595,"mutability":"mutable","name":"x","nameLocation":"5753:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5746:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":594,"nodeType":"UserDefinedTypeName","pathNode":{"id":593,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5746:6:0"},"referencedDeclaration":799,"src":"5746:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":601,"initialValue":{"arguments":[{"hexValue":"31323334","id":599,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5768:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":598,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5757:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":597,"nodeType":"UserDefinedTypeName","pathNode":{"id":596,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5761:6:0"},"referencedDeclaration":799,"src":"5761:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":600,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5757:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5746:27:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":607,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":603,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":595,"src":"5791:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":604,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"5791:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":605,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5791:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":606,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5802:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"5791:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e20637265617465206973206e6f742031323334","id":608,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5808:35:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""},"value":"FooBar: foo in create is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_cf44a206a1b0f98235522779025d2df914f464e764b8c79ccaa1efde72c4831c","typeString":"literal_string \"FooBar: foo in create is not 1234\""}],"id":602,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"5783:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":609,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5783:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":610,"nodeType":"ExpressionStatement","src":"5783:61:0"},{"expression":{"arguments":[{"hexValue":"6372656174652032","id":614,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"5867:10:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""},"value":"create 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_4411d6d4ffcd00382a95255a63761e69de9810e1236042a5c64948a7b6c04daa","typeString":"literal_string \"create 2\""}],"expression":{"id":611,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"5855:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":613,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"5855:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":615,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5855:23:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":616,"nodeType":"ExpressionStatement","src":"5855:23:0"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"hexValue":"307863616665","id":624,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5917:6:0","typeDescriptions":{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"},"value":"0xcafe"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_51966_by_1","typeString":"int_const 51966"}],"id":623,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5909:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":622,"name":"uint160","nodeType":"ElementaryTypeName","src":"5909:7:0","typeDescriptions":{}}},"id":625,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5909:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":621,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5901:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":620,"name":"address","nodeType":"ElementaryTypeName","src":"5901:7:0","typeDescriptions":{}}},"id":626,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5901:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":617,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"5888:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":619,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":43,"src":"5888:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_address_$returns$__$","typeString":"function (address) external"}},"id":627,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5888:38:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":628,"nodeType":"ExpressionStatement","src":"5888:38:0"},{"assignments":[631],"declarations":[{"constant":false,"id":631,"mutability":"mutable","name":"y","nameLocation":"5943:1:0","nodeType":"VariableDeclaration","scope":688,"src":"5936:8:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"},"typeName":{"id":630,"nodeType":"UserDefinedTypeName","pathNode":{"id":629,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5936:6:0"},"referencedDeclaration":799,"src":"5936:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"visibility":"internal"}],"id":645,"initialValue":{"arguments":[{"hexValue":"31323334","id":643,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5986:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":634,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"5947:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":633,"nodeType":"UserDefinedTypeName","pathNode":{"id":632,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"5951:6:0"},"referencedDeclaration":799,"src":"5951:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":642,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"names":["salt"],"nodeType":"FunctionCallOptions","options":[{"arguments":[{"arguments":[{"hexValue":"3432","id":639,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"5980:2:0","typeDescriptions":{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"},"value":"42"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_42_by_1","typeString":"int_const 42"}],"id":638,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5972:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":637,"name":"uint256","nodeType":"ElementaryTypeName","src":"5972:7:0","typeDescriptions":{}}},"id":640,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5972:11:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":636,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"5964:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_bytes32_$","typeString":"type(bytes32)"},"typeName":{"id":635,"name":"bytes32","nodeType":"ElementaryTypeName","src":"5964:7:0","typeDescriptions":{}}},"id":641,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5964:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"src":"5947:38:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$salt","typeString":"function (uint256) returns (contract FooBar)"}},"id":644,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"5947:44:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"nodeType":"VariableDeclarationStatement","src":"5936:55:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":651,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":647,"name":"y","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":631,"src":"6009:1:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":648,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"foo","nodeType":"MemberAccess","referencedDeclaration":788,"src":"6009:5:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":649,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6009:7:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31323334","id":650,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6020:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"},"src":"6009:15:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"466f6f4261723a20666f6f20696e2063726561746532206973206e6f742031323334","id":652,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6026:36:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""},"value":"FooBar: foo in create2 is not 1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_a532f8073e029b895a819f6b1992843ca1cc824c13ad4c6484e05780ac0a57b9","typeString":"literal_string \"FooBar: foo in create2 is not 1234\""}],"id":646,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"6001:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":653,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6001:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":654,"nodeType":"ExpressionStatement","src":"6001:62:0"},{"expression":{"arguments":[{"hexValue":"646f6e6521","id":658,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6085:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""},"value":"done!"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_080382d5c9e9e7c5e3d1d33f5e7422740375955180fadff167d8130e0c35f3fc","typeString":"literal_string \"done!\""}],"expression":{"id":655,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6073:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":657,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6073:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":659,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6073:20:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":660,"nodeType":"ExpressionStatement","src":"6073:20:0"},{"expression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":661,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6177:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":663,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"broadcast","nodeType":"MemberAccess","referencedDeclaration":38,"src":"6177:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$__$returns$__$","typeString":"function () external"}},"id":664,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6177:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":665,"nodeType":"ExpressionStatement","src":"6177:14:0"},{"expression":{"arguments":[{"hexValue":"31323334","id":669,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"6212:4:0","typeDescriptions":{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"},"value":"1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1234_by_1","typeString":"int_const 1234"}],"id":668,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"NewExpression","src":"6201:10:0","typeDescriptions":{"typeIdentifier":"t_function_creation_nonpayable$_t_uint256_$returns$_t_contract$_FooBar_$799_$","typeString":"function (uint256) returns (contract FooBar)"},"typeName":{"id":667,"nodeType":"UserDefinedTypeName","pathNode":{"id":666,"name":"FooBar","nodeType":"IdentifierPath","referencedDeclaration":799,"src":"6205:6:0"},"referencedDeclaration":799,"src":"6205:6:0","typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}}},"id":670,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6201:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_FooBar_$799","typeString":"contract FooBar"}},"id":671,"nodeType":"ExpressionStatement","src":"6201:16:0"},{"expression":{"arguments":[{"hexValue":"6e6f6e636520656e64","id":675,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6240:11:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},"value":"nonce end"},{"arguments":[{"arguments":[{"arguments":[{"id":682,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6281:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}],"id":681,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6273:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":680,"name":"address","nodeType":"ElementaryTypeName","src":"6273:7:0","typeDescriptions":{}}},"id":683,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6273:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":678,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":241,"src":"6261:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":679,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"6261:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":684,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6261:26:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint64","typeString":"uint64"}],"id":677,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6253:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":676,"name":"uint256","nodeType":"ElementaryTypeName","src":"6253:7:0","typeDescriptions":{}}},"id":685,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6253:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_fa629e6661ad2a2bdb09cf9a3a276ce0d722482ae5c2887650751be0938847e8","typeString":"literal_string \"nonce end\""},{"typeIdentifier":"t_uint256","typeString":"uint256"}],"expression":{"id":672,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6228:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":674,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":182,"src":"6228:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_uint256_$returns$__$","typeString":"function (string memory,uint256) pure"}},"id":686,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6228:61:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":687,"nodeType":"ExpressionStatement","src":"6228:61:0"}]},"documentation":{"id":457,"nodeType":"StructuredDocumentation","src":"4902:56:0","text":"@notice example function, to test vm.broadcast with."},"functionSelector":"bef03abc","implemented":true,"kind":"function","modifiers":[],"name":"runBroadcast","nameLocation":"4972:12:0","parameters":{"id":458,"nodeType":"ParameterList","parameters":[],"src":"4984:2:0"},"returnParameters":{"id":459,"nodeType":"ParameterList","parameters":[],"src":"4994:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":713,"nodeType":"FunctionDefinition","src":"6391:143:0","nodes":[],"body":{"id":712,"nodeType":"Block","src":"6440:94:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":698,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":692,"src":"6462:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":695,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6450:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":697,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6450:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":699,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6450:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":700,"nodeType":"ExpressionStatement","src":"6450:15:0"},{"expression":{"arguments":[{"hexValue":"68656c6c6f206d73672e73656e646572","id":704,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"6487:18:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},"value":"hello msg.sender"},{"arguments":[{"expression":{"id":707,"name":"msg","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-15,"src":"6515:3:0","typeDescriptions":{"typeIdentifier":"t_magic_message","typeString":"msg"}},"id":708,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"sender","nodeType":"MemberAccess","src":"6515:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":706,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"6507:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":705,"name":"address","nodeType":"ElementaryTypeName","src":"6507:7:0","typeDescriptions":{}}},"id":709,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6507:19:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b3cc13bc51228b2c4c4334d82a4772908254dc0e1c512893dd16208ef13efb8e","typeString":"literal_string \"hello msg.sender\""},{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":701,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6475:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":703,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":199,"src":"6475:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$_t_address_$returns$__$","typeString":"function (string memory,address) pure"}},"id":710,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6475:52:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":711,"nodeType":"ExpressionStatement","src":"6475:52:0"}]},"documentation":{"id":690,"nodeType":"StructuredDocumentation","src":"6302:84:0","text":"@notice example external function, to force a CALL, and test vm.startPrank with."},"functionSelector":"a777d0dc","implemented":true,"kind":"function","modifiers":[],"name":"hello","nameLocation":"6400:5:0","parameters":{"id":693,"nodeType":"ParameterList","parameters":[{"constant":false,"id":692,"mutability":"mutable","name":"_v","nameLocation":"6422:2:0","nodeType":"VariableDeclaration","scope":713,"src":"6406:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":691,"name":"string","nodeType":"ElementaryTypeName","src":"6406:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6405:20:0"},"returnParameters":{"id":694,"nodeType":"ParameterList","parameters":[],"src":"6440:0:0"},"scope":786,"stateMutability":"view","virtual":false,"visibility":"external"},{"id":728,"nodeType":"FunctionDefinition","src":"6540:95:0","nodes":[],"body":{"id":727,"nodeType":"Block","src":"6584:51:0","nodes":[],"statements":[{"expression":{"id":719,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6594:9:0","subExpression":{"id":718,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6594:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":720,"nodeType":"ExpressionStatement","src":"6594:9:0"},{"expression":{"arguments":[{"id":724,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":715,"src":"6625:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":721,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6613:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":723,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6613:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":725,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6613:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":726,"nodeType":"ExpressionStatement","src":"6613:15:0"}]},"functionSelector":"7e79255d","implemented":true,"kind":"function","modifiers":[],"name":"call1","nameLocation":"6549:5:0","parameters":{"id":716,"nodeType":"ParameterList","parameters":[{"constant":false,"id":715,"mutability":"mutable","name":"_v","nameLocation":"6571:2:0","nodeType":"VariableDeclaration","scope":728,"src":"6555:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":714,"name":"string","nodeType":"ElementaryTypeName","src":"6555:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6554:20:0"},"returnParameters":{"id":717,"nodeType":"ParameterList","parameters":[],"src":"6584:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":743,"nodeType":"FunctionDefinition","src":"6641:95:0","nodes":[],"body":{"id":742,"nodeType":"Block","src":"6685:51:0","nodes":[],"statements":[{"expression":{"id":734,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6695:9:0","subExpression":{"id":733,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6695:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":735,"nodeType":"ExpressionStatement","src":"6695:9:0"},{"expression":{"arguments":[{"id":739,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":730,"src":"6726:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":736,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6714:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":738,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6714:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":740,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6714:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":741,"nodeType":"ExpressionStatement","src":"6714:15:0"}]},"functionSelector":"8d3ef7ca","implemented":true,"kind":"function","modifiers":[],"name":"call2","nameLocation":"6650:5:0","parameters":{"id":731,"nodeType":"ParameterList","parameters":[{"constant":false,"id":730,"mutability":"mutable","name":"_v","nameLocation":"6672:2:0","nodeType":"VariableDeclaration","scope":743,"src":"6656:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":729,"name":"string","nodeType":"ElementaryTypeName","src":"6656:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6655:20:0"},"returnParameters":{"id":732,"nodeType":"ParameterList","parameters":[],"src":"6685:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":758,"nodeType":"FunctionDefinition","src":"6742:98:0","nodes":[],"body":{"id":757,"nodeType":"Block","src":"6788:52:0","nodes":[],"statements":[{"expression":{"id":749,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6798:9:0","subExpression":{"id":748,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6798:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":750,"nodeType":"ExpressionStatement","src":"6798:9:0"},{"expression":{"arguments":[{"id":754,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":745,"src":"6830:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":751,"name":"this","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-28,"src":"6817:4:0","typeDescriptions":{"typeIdentifier":"t_contract$_ScriptExample_$786","typeString":"contract ScriptExample"}},"id":753,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"nested2","nodeType":"MemberAccess","referencedDeclaration":773,"src":"6817:12:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) external"}},"id":755,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6817:16:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":756,"nodeType":"ExpressionStatement","src":"6817:16:0"}]},"functionSelector":"a76ccdfa","implemented":true,"kind":"function","modifiers":[],"name":"nested1","nameLocation":"6751:7:0","parameters":{"id":746,"nodeType":"ParameterList","parameters":[{"constant":false,"id":745,"mutability":"mutable","name":"_v","nameLocation":"6775:2:0","nodeType":"VariableDeclaration","scope":758,"src":"6759:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":744,"name":"string","nodeType":"ElementaryTypeName","src":"6759:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6758:20:0"},"returnParameters":{"id":747,"nodeType":"ParameterList","parameters":[],"src":"6788:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":773,"nodeType":"FunctionDefinition","src":"6846:97:0","nodes":[],"body":{"id":772,"nodeType":"Block","src":"6892:51:0","nodes":[],"statements":[{"expression":{"id":764,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"6902:9:0","subExpression":{"id":763,"name":"counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":243,"src":"6902:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":765,"nodeType":"ExpressionStatement","src":"6902:9:0"},{"expression":{"arguments":[{"id":769,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":760,"src":"6933:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":766,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"6921:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":768,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"6921:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":770,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"6921:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":771,"nodeType":"ExpressionStatement","src":"6921:15:0"}]},"functionSelector":"dbf1282f","implemented":true,"kind":"function","modifiers":[],"name":"nested2","nameLocation":"6855:7:0","parameters":{"id":761,"nodeType":"ParameterList","parameters":[{"constant":false,"id":760,"mutability":"mutable","name":"_v","nameLocation":"6879:2:0","nodeType":"VariableDeclaration","scope":773,"src":"6863:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":759,"name":"string","nodeType":"ElementaryTypeName","src":"6863:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6862:20:0"},"returnParameters":{"id":762,"nodeType":"ParameterList","parameters":[],"src":"6892:0:0"},"scope":786,"stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"id":785,"nodeType":"FunctionDefinition","src":"6949:84:0","nodes":[],"body":{"id":784,"nodeType":"Block","src":"7001:32:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":781,"name":"_v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":775,"src":"7023:2:0","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_string_calldata_ptr","typeString":"string calldata"}],"expression":{"id":778,"name":"console","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":220,"src":"7011:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_console_$220_$","typeString":"type(library console)"}},"id":780,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"log","nodeType":"MemberAccess","referencedDeclaration":148,"src":"7011:11:0","typeDescriptions":{"typeIdentifier":"t_function_internal_pure$_t_string_memory_ptr_$returns$__$","typeString":"function (string memory) pure"}},"id":782,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7011:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":783,"nodeType":"ExpressionStatement","src":"7011:15:0"}]},"functionSelector":"7f8b915c","implemented":true,"kind":"function","modifiers":[],"name":"callPure","nameLocation":"6958:8:0","parameters":{"id":776,"nodeType":"ParameterList","parameters":[{"constant":false,"id":775,"mutability":"mutable","name":"_v","nameLocation":"6983:2:0","nodeType":"VariableDeclaration","scope":785,"src":"6967:18:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_string_calldata_ptr","typeString":"string"},"typeName":{"id":774,"name":"string","nodeType":"ElementaryTypeName","src":"6967:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"6966:20:0"},"returnParameters":{"id":777,"nodeType":"ParameterList","parameters":[],"src":"7001:0:0"},"scope":786,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ScriptExample","contractDependencies":[799],"contractKind":"contract","documentation":{"id":221,"nodeType":"StructuredDocumentation","src":"2997:126:0","text":"@title ScriptExample\n @notice ScriptExample is an example script. The Go forge script code tests that it can run this."},"fullyImplemented":true,"linearizedBaseContracts":[786],"name":"ScriptExample","nameLocation":"3132:13:0","scope":969,"usedErrors":[]},{"id":799,"nodeType":"ContractDefinition","src":"7037:96:0","nodes":[{"id":788,"nodeType":"VariableDeclaration","src":"7059:18:0","nodes":[],"constant":false,"functionSelector":"c2985578","mutability":"mutable","name":"foo","nameLocation":"7074:3:0","scope":799,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":787,"name":"uint256","nodeType":"ElementaryTypeName","src":"7059:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":798,"nodeType":"FunctionDefinition","src":"7084:47:0","nodes":[],"body":{"id":797,"nodeType":"Block","src":"7107:24:0","nodes":[],"statements":[{"expression":{"id":795,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":793,"name":"foo","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":788,"src":"7117:3:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":794,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":790,"src":"7123:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"7117:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":796,"nodeType":"ExpressionStatement","src":"7117:7:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":791,"nodeType":"ParameterList","parameters":[{"constant":false,"id":790,"mutability":"mutable","name":"v","nameLocation":"7104:1:0","nodeType":"VariableDeclaration","scope":798,"src":"7096:9:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":789,"name":"uint256","nodeType":"ElementaryTypeName","src":"7096:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7095:11:0"},"returnParameters":{"id":792,"nodeType":"ParameterList","parameters":[],"src":"7107:0:0"},"scope":799,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"FooBar","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[799],"name":"FooBar","nameLocation":"7046:6:0","scope":969,"usedErrors":[]},{"id":833,"nodeType":"ContractDefinition","src":"7135:281:0","nodes":[{"id":813,"nodeType":"VariableDeclaration","src":"7162:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7188:10:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":800,"name":"address","nodeType":"ElementaryTypeName","src":"7162:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":808,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7235:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":807,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7225:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":809,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7225:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":806,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7217:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":805,"name":"uint256","nodeType":"ElementaryTypeName","src":"7217:7:0","typeDescriptions":{}}},"id":810,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7217:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":804,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7209:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":803,"name":"uint160","nodeType":"ElementaryTypeName","src":"7209:7:0","typeDescriptions":{}}},"id":811,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7209:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":802,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7201:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":801,"name":"address","nodeType":"ElementaryTypeName","src":"7201:7:0","typeDescriptions":{}}},"id":812,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7201:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":819,"nodeType":"VariableDeclaration","src":"7262:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7283:2:0","scope":833,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":815,"nodeType":"UserDefinedTypeName","pathNode":{"id":814,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7262:2:0"},"referencedDeclaration":83,"src":"7262:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":817,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":813,"src":"7291:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":816,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7288:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":818,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7288:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":832,"nodeType":"FunctionDefinition","src":"7309:105:0","nodes":[],"body":{"id":831,"nodeType":"Block","src":"7372:42:0","nodes":[],"statements":[{"expression":{"arguments":[{"id":828,"name":"_addr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":821,"src":"7401:5:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":826,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":819,"src":"7389:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":827,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7389:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":829,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7389:18:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"functionReturnParameters":825,"id":830,"nodeType":"Return","src":"7382:25:0"}]},"functionSelector":"2d0335ab","implemented":true,"kind":"function","modifiers":[],"name":"getNonce","nameLocation":"7318:8:0","parameters":{"id":822,"nodeType":"ParameterList","parameters":[{"constant":false,"id":821,"mutability":"mutable","name":"_addr","nameLocation":"7335:5:0","nodeType":"VariableDeclaration","scope":832,"src":"7327:13:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":820,"name":"address","nodeType":"ElementaryTypeName","src":"7327:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"7326:15:0"},"returnParameters":{"id":825,"nodeType":"ParameterList","parameters":[{"constant":false,"id":824,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":832,"src":"7363:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":823,"name":"uint256","nodeType":"ElementaryTypeName","src":"7363:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7362:9:0"},"scope":833,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"NonceGetter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[833],"name":"NonceGetter","nameLocation":"7144:11:0","scope":969,"usedErrors":[]},{"id":852,"nodeType":"ContractDefinition","src":"7418:174:0","nodes":[{"id":835,"nodeType":"VariableDeclaration","src":"7448:18:0","nodes":[],"constant":false,"mutability":"mutable","name":"v","nameLocation":"7465:1:0","scope":852,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":834,"name":"uint256","nodeType":"ElementaryTypeName","src":"7448:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"id":843,"nodeType":"FunctionDefinition","src":"7473:36:0","nodes":[],"body":{"id":842,"nodeType":"Block","src":"7487:22:0","nodes":[],"statements":[{"expression":{"id":840,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":838,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7497:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"hexValue":"31","id":839,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7501:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"7497:5:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":841,"nodeType":"ExpressionStatement","src":"7497:5:0"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":836,"nodeType":"ParameterList","parameters":[],"src":"7484:2:0"},"returnParameters":{"id":837,"nodeType":"ParameterList","parameters":[],"src":"7487:0:0"},"scope":852,"stateMutability":"nonpayable","virtual":false,"visibility":"public"},{"id":851,"nodeType":"FunctionDefinition","src":"7515:75:0","nodes":[],"body":{"id":850,"nodeType":"Block","src":"7565:25:0","nodes":[],"statements":[{"expression":{"id":848,"name":"v","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":835,"src":"7582:1:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":847,"id":849,"nodeType":"Return","src":"7575:8:0"}]},"functionSelector":"20965255","implemented":true,"kind":"function","modifiers":[],"name":"getValue","nameLocation":"7524:8:0","parameters":{"id":844,"nodeType":"ParameterList","parameters":[],"src":"7532:2:0"},"returnParameters":{"id":847,"nodeType":"ParameterList","parameters":[{"constant":false,"id":846,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":851,"src":"7556:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":845,"name":"uint256","nodeType":"ElementaryTypeName","src":"7556:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"7555:9:0"},"scope":852,"stateMutability":"view","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkedContract","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[852],"name":"ForkedContract","nameLocation":"7427:14:0","scope":969,"usedErrors":[]},{"id":968,"nodeType":"ContractDefinition","src":"7594:813:0","nodes":[{"id":866,"nodeType":"VariableDeclaration","src":"7620:94:0","nodes":[],"constant":true,"mutability":"constant","name":"VM_ADDRESS","nameLocation":"7646:10:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":853,"name":"address","nodeType":"ElementaryTypeName","src":"7620:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"value":{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"hexValue":"6865766d20636865617420636f6465","id":861,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7693:17:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""},"value":"hevm cheat code"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d","typeString":"literal_string \"hevm cheat code\""}],"id":860,"name":"keccak256","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":-8,"src":"7683:9:0","typeDescriptions":{"typeIdentifier":"t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$","typeString":"function (bytes memory) pure returns (bytes32)"}},"id":862,"isConstant":false,"isLValue":false,"isPure":true,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7683:28:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_bytes32","typeString":"bytes32"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes32","typeString":"bytes32"}],"id":859,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7675:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":858,"name":"uint256","nodeType":"ElementaryTypeName","src":"7675:7:0","typeDescriptions":{}}},"id":863,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7675:37:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint256","typeString":"uint256"}],"id":857,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7667:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":856,"name":"uint160","nodeType":"ElementaryTypeName","src":"7667:7:0","typeDescriptions":{}}},"id":864,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7667:46:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":855,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7659:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":854,"name":"address","nodeType":"ElementaryTypeName","src":"7659:7:0","typeDescriptions":{}}},"id":865,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7659:55:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"id":872,"nodeType":"VariableDeclaration","src":"7720:40:0","nodes":[],"constant":true,"mutability":"constant","name":"vm","nameLocation":"7741:2:0","scope":968,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"},"typeName":{"id":868,"nodeType":"UserDefinedTypeName","pathNode":{"id":867,"name":"Vm","nodeType":"IdentifierPath","referencedDeclaration":83,"src":"7720:2:0"},"referencedDeclaration":83,"src":"7720:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"value":{"arguments":[{"id":870,"name":"VM_ADDRESS","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":866,"src":"7749:10:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":869,"name":"Vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":83,"src":"7746:2:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_Vm_$83_$","typeString":"type(contract Vm)"}},"id":871,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7746:14:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"visibility":"internal"},{"id":967,"nodeType":"FunctionDefinition","src":"7767:638:0","nodes":[],"body":{"id":966,"nodeType":"Block","src":"7791:614:0","nodes":[],"statements":[{"assignments":[876],"declarations":[{"constant":false,"id":876,"mutability":"mutable","name":"testAddr","nameLocation":"7809:8:0","nodeType":"VariableDeclaration","scope":966,"src":"7801:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":875,"name":"address","nodeType":"ElementaryTypeName","src":"7801:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"id":884,"initialValue":{"arguments":[{"arguments":[{"hexValue":"307831323334","id":881,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7836:6:0","typeDescriptions":{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"},"value":"0x1234"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_4660_by_1","typeString":"int_const 4660"}],"id":880,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7828:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint160_$","typeString":"type(uint160)"},"typeName":{"id":879,"name":"uint160","nodeType":"ElementaryTypeName","src":"7828:7:0","typeDescriptions":{}}},"id":882,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7828:15:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint160","typeString":"uint160"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_uint160","typeString":"uint160"}],"id":878,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"7820:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_address_$","typeString":"type(address)"},"typeName":{"id":877,"name":"address","nodeType":"ElementaryTypeName","src":"7820:7:0","typeDescriptions":{}}},"id":883,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7820:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"nodeType":"VariableDeclarationStatement","src":"7801:43:0"},{"assignments":[887],"declarations":[{"constant":false,"id":887,"mutability":"mutable","name":"fc","nameLocation":"7869:2:0","nodeType":"VariableDeclaration","scope":966,"src":"7854:17:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"},"typeName":{"id":886,"nodeType":"UserDefinedTypeName","pathNode":{"id":885,"name":"ForkedContract","nodeType":"IdentifierPath","referencedDeclaration":852,"src":"7854:14:0"},"referencedDeclaration":852,"src":"7854:14:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"visibility":"internal"}],"id":891,"initialValue":{"arguments":[{"id":889,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7889:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"id":888,"name":"ForkedContract","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":852,"src":"7874:14:0","typeDescriptions":{"typeIdentifier":"t_type$_t_contract$_ForkedContract_$852_$","typeString":"type(contract ForkedContract)"}},"id":890,"isConstant":false,"isLValue":false,"isPure":false,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7874:24:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"nodeType":"VariableDeclarationStatement","src":"7854:44:0"},{"expression":{"arguments":[{"hexValue":"666f726b31","id":895,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7929:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},"value":"fork1"},{"hexValue":"3132333435","id":896,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7938:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_b6acbb7ba3bf910295048af2ccd655ff20a445d705d49fd56157c24aab14c1a1","typeString":"literal_string \"fork1\""},{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"}],"expression":{"id":892,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7909:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":894,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"7909:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":897,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7909:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":898,"nodeType":"ExpressionStatement","src":"7909:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":905,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":902,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"7974:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":900,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"7962:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":901,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"7962:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":903,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7962:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3132333435","id":904,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"7987:5:0","typeDescriptions":{"typeIdentifier":"t_rational_12345_by_1","typeString":"int_const 12345"},"value":"12345"},"src":"7962:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":906,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"7994:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":899,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"7954:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":907,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"7954:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":908,"nodeType":"ExpressionStatement","src":"7954:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":914,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":910,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8036:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":911,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8036:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":912,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8036:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"31","id":913,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8053:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"8036:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652031","id":915,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8056:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""},"value":"value should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_244bd39a8f8426ed26a6cae45b2ada0383deda0bbc513dfe29f31ab8529d5c7a","typeString":"literal_string \"value should be 1\""}],"id":909,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8028:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":916,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8028:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":917,"nodeType":"ExpressionStatement","src":"8028:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":925,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":919,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8094:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":920,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8094:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"31","id":923,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8122:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"}],"id":922,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8114:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":921,"name":"uint256","nodeType":"ElementaryTypeName","src":"8114:7:0","typeDescriptions":{}}},"id":924,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8114:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8094:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652031","id":926,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8126:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""},"value":"balance should be 1"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675b86838b72d956fe80c51e424164ea5e48d46b089cf53543fefe5ee2c684bf","typeString":"literal_string \"balance should be 1\""}],"id":918,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8086:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":927,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8086:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":928,"nodeType":"ExpressionStatement","src":"8086:62:0"},{"expression":{"arguments":[{"hexValue":"666f726b32","id":932,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8179:7:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},"value":"fork2"},{"hexValue":"3233343536","id":933,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8188:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_stringliteral_261b052a4950a8ec6afce52cd61229704be48859b7177f79ca612a21277827f8","typeString":"literal_string \"fork2\""},{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"}],"expression":{"id":929,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8159:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":931,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"createSelectFork","nodeType":"MemberAccess","referencedDeclaration":82,"src":"8159:19:0","typeDescriptions":{"typeIdentifier":"t_function_external_nonpayable$_t_string_memory_ptr_$_t_uint256_$returns$_t_uint256_$","typeString":"function (string memory,uint256) external returns (uint256)"}},"id":934,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8159:35:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":935,"nodeType":"ExpressionStatement","src":"8159:35:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint64","typeString":"uint64"},"id":942,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[{"id":939,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8224:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_address","typeString":"address"}],"expression":{"id":937,"name":"vm","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":872,"src":"8212:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_Vm_$83","typeString":"contract Vm"}},"id":938,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getNonce","nodeType":"MemberAccess","referencedDeclaration":17,"src":"8212:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$_t_address_$returns$_t_uint64_$","typeString":"function (address) view external returns (uint64)"}},"id":940,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8212:21:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint64","typeString":"uint64"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"3233343536","id":941,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8237:5:0","typeDescriptions":{"typeIdentifier":"t_rational_23456_by_1","typeString":"int_const 23456"},"value":"23456"},"src":"8212:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"6e6f6e63652073686f756c64206265203132333435","id":943,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8244:23:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""},"value":"nonce should be 12345"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_675408ff346f993e251ba3ee09efb90c23d0de302269ea6afde722ac077acbdb","typeString":"literal_string \"nonce should be 12345\""}],"id":936,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8204:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":944,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8204:64:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":945,"nodeType":"ExpressionStatement","src":"8204:64:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":951,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"arguments":[],"expression":{"argumentTypes":[],"expression":{"id":947,"name":"fc","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":887,"src":"8286:2:0","typeDescriptions":{"typeIdentifier":"t_contract$_ForkedContract_$852","typeString":"contract ForkedContract"}},"id":948,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"getValue","nodeType":"MemberAccess","referencedDeclaration":851,"src":"8286:11:0","typeDescriptions":{"typeIdentifier":"t_function_external_view$__$returns$_t_uint256_$","typeString":"function () view external returns (uint256)"}},"id":949,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8286:13:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"hexValue":"32","id":950,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8303:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"8286:18:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"76616c75652073686f756c642062652032","id":952,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8306:19:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""},"value":"value should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_989f7bdcf9093cc756fd0c37255cb127d8c8369545d3f3456d0571522c208b6d","typeString":"literal_string \"value should be 2\""}],"id":946,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8278:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":953,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8278:48:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":954,"nodeType":"ExpressionStatement","src":"8278:48:0"},{"expression":{"arguments":[{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":962,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"expression":{"id":956,"name":"testAddr","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":876,"src":"8344:8:0","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"id":957,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"memberName":"balance","nodeType":"MemberAccess","src":"8344:16:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"==","rightExpression":{"arguments":[{"hexValue":"32","id":960,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"8372:1:0","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"}],"id":959,"isConstant":false,"isLValue":false,"isPure":true,"lValueRequested":false,"nodeType":"ElementaryTypeNameExpression","src":"8364:7:0","typeDescriptions":{"typeIdentifier":"t_type$_t_uint256_$","typeString":"type(uint256)"},"typeName":{"id":958,"name":"uint256","nodeType":"ElementaryTypeName","src":"8364:7:0","typeDescriptions":{}}},"id":961,"isConstant":false,"isLValue":false,"isPure":true,"kind":"typeConversion","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8364:10:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"8344:30:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},{"hexValue":"62616c616e63652073686f756c642062652032","id":963,"isConstant":false,"isLValue":false,"isPure":true,"kind":"string","lValueRequested":false,"nodeType":"Literal","src":"8376:21:0","typeDescriptions":{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""},"value":"balance should be 2"}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bool","typeString":"bool"},{"typeIdentifier":"t_stringliteral_4c35c4bb929f7c1c753e1326d2d04380b315ea3b8a63106213ab37dd0832958a","typeString":"literal_string \"balance should be 2\""}],"id":955,"name":"require","nodeType":"Identifier","overloadedDeclarations":[-18,-18],"referencedDeclaration":-18,"src":"8336:7:0","typeDescriptions":{"typeIdentifier":"t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$","typeString":"function (bool,string memory) pure"}},"id":964,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"names":[],"nodeType":"FunctionCall","src":"8336:62:0","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":965,"nodeType":"ExpressionStatement","src":"8336:62:0"}]},"functionSelector":"c0406226","implemented":true,"kind":"function","modifiers":[],"name":"run","nameLocation":"7776:3:0","parameters":{"id":873,"nodeType":"ParameterList","parameters":[],"src":"7779:2:0"},"returnParameters":{"id":874,"nodeType":"ParameterList","parameters":[],"src":"7791:0:0"},"scope":968,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ForkTester","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[968],"name":"ForkTester","nameLocation":"7603:10:0","scope":969,"usedErrors":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
diff --git a/op-chain-ops/script/with.go b/op-chain-ops/script/with.go
index 00560d5a6b6e..d70762a61f8e 100644
--- a/op-chain-ops/script/with.go
+++ b/op-chain-ops/script/with.go
@@ -3,6 +3,8 @@ package script
 import (
 	"fmt"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
+
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
@@ -26,10 +28,13 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun
 		return nil, nil, fmt.Errorf("could not load script artifact: %w", err)
 	}
 
-	deployer := ScriptDeployer
+	deployer := addresses.ScriptDeployer
 	deployNonce := h.state.GetNonce(deployer)
 	// compute address of script contract to be deployed
 	addr := crypto.CreateAddress(deployer, deployNonce)
+	h.Label(addr, contract)
+	h.AllowCheatcodes(addr)      // before constructor execution, give our script cheatcode access
+	h.state.MakePersistent(addr) // scripts are persistent across forks
 
 	// init bindings (with ABI check)
 	bindings, err := MakeBindings[B](h.ScriptBackendFn(addr), func(abiDef string) bool {
@@ -51,7 +56,6 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun
 		return nil, nil, fmt.Errorf("deployed to unexpected address %s, expected %s", deployedAddr, addr)
 	}
 	h.RememberArtifact(addr, artifact, contract)
-	h.Label(addr, contract)
 	return bindings, func() {
 		h.Wipe(addr)
 	}, nil

From 2acb4973cbce8709a15d009b050e9abd701bfcf8 Mon Sep 17 00:00:00 2001
From: Michael Amadi 
Date: Wed, 13 Nov 2024 13:29:24 +0100
Subject: [PATCH 173/451] cleanup (#12913)

---
 .../contracts-bedrock/scripts/checks/check-semver-diff.sh   | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh
index 62872e1e73d8..70304f641690 100755
--- a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh
+++ b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh
@@ -23,10 +23,8 @@ fi
 
 # Get the upstream semver-lock.json.
 if ! git show origin/develop:packages/contracts-bedrock/snapshots/semver-lock.json > "$temp_dir/upstream_semver_lock.json" 2>/dev/null; then
-    if ! git show origin/develop:packages/contracts-bedrock/semver-lock.json > "$temp_dir/upstream_semver_lock.json" 2>/dev/null; then
-        echo "❌ Error: Could not find semver-lock.json in either snapshots/ or root directory of develop branch"
-        exit 1
-    fi
+      echo "❌ Error: Could not find semver-lock.json in the snapshots/ directory of develop branch"
+      exit 1
 fi
 
 # Copy the local semver-lock.json.

From f549204b026c604f6f56de82504f44c725ef081d Mon Sep 17 00:00:00 2001
From: Sebastian Stammler 
Date: Wed, 13 Nov 2024 17:57:46 +0100
Subject: [PATCH 174/451] Prepare Holocene testnet & devnet release (#12917)

---
 go.mod                          |  4 +--
 go.sum                          |  8 ++---
 op-node/chaincfg/chains_test.go | 64 ++++++++++++++++-----------------
 op-node/rollup/superchain.go    |  2 +-
 4 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/go.mod b/go.mod
index d077cf6a8bb0..d1be77ddc0e1 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/crate-crypto/go-kzg-4844 v1.0.0
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
 	github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3
-	github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac
+	github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7
 	github.com/ethereum/go-ethereum v1.14.11
 	github.com/fsnotify/fsnotify v1.8.0
 	github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
@@ -250,7 +250,7 @@ require (
 	rsc.io/tmplfunc v0.0.3 // indirect
 )
 
-replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.6
+replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.2-rc.1
 
 //replace github.com/ethereum/go-ethereum => ../go-ethereum
 
diff --git a/go.sum b/go.sum
index d9f532807675..5eabf529b0df 100644
--- a/go.sum
+++ b/go.sum
@@ -187,10 +187,10 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u
 github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
-github.com/ethereum-optimism/op-geth v1.101411.1-rc.6 h1:VvUBIVFbnU9486CWHa9Js5XYY3o6OsdQcI8gE3XjCDE=
-github.com/ethereum-optimism/op-geth v1.101411.1-rc.6/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4=
-github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg=
-github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M=
+github.com/ethereum-optimism/op-geth v1.101411.2-rc.1 h1:v314tR5EzG+QNE9aLf+goWCDsTT+RT2EsdOOlJT6CwM=
+github.com/ethereum-optimism/op-geth v1.101411.2-rc.1/go.mod h1:RrPkuqfeIXkW28lQJwc5AG/BKbhkHRXPD5YezeeK4w8=
+github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7 h1:Mbgsp5T52F2pEULHccLr4NtnT6cKnJgabpAPlTfPxrk=
+github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs=
 github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
 github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
 github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
diff --git a/op-node/chaincfg/chains_test.go b/op-node/chaincfg/chains_test.go
index 7499aa4ba3ee..bd9956253cd5 100644
--- a/op-node/chaincfg/chains_test.go
+++ b/op-node/chaincfg/chains_test.go
@@ -89,22 +89,22 @@ var sepoliaCfg = rollup.Config{
 			GasLimit:    30000000,
 		},
 	},
-	BlockTime:              2,
-	MaxSequencerDrift:      600,
-	SeqWindowSize:          3600,
-	ChannelTimeoutBedrock:  300,
-	L1ChainID:              big.NewInt(11155111),
-	L2ChainID:              big.NewInt(11155420),
-	BatchInboxAddress:      common.HexToAddress("0xff00000000000000000000000000000011155420"),
-	DepositContractAddress: common.HexToAddress("0x16fc5058f25648194471939df75cf27a2fdc48bc"),
-	L1SystemConfigAddress:  common.HexToAddress("0x034edd2a225f7f429a63e0f1d2084b9e0a93b538"),
-	RegolithTime:           u64Ptr(0),
-	CanyonTime:             u64Ptr(1699981200),
-	DeltaTime:              u64Ptr(1703203200),
-	EcotoneTime:            u64Ptr(1708534800),
-	FjordTime:              u64Ptr(1716998400),
-	GraniteTime:            u64Ptr(1723478400),
-	// HoloceneTime: TBD
+	BlockTime:               2,
+	MaxSequencerDrift:       600,
+	SeqWindowSize:           3600,
+	ChannelTimeoutBedrock:   300,
+	L1ChainID:               big.NewInt(11155111),
+	L2ChainID:               big.NewInt(11155420),
+	BatchInboxAddress:       common.HexToAddress("0xff00000000000000000000000000000011155420"),
+	DepositContractAddress:  common.HexToAddress("0x16fc5058f25648194471939df75cf27a2fdc48bc"),
+	L1SystemConfigAddress:   common.HexToAddress("0x034edd2a225f7f429a63e0f1d2084b9e0a93b538"),
+	RegolithTime:            u64Ptr(0),
+	CanyonTime:              u64Ptr(1699981200),
+	DeltaTime:               u64Ptr(1703203200),
+	EcotoneTime:             u64Ptr(1708534800),
+	FjordTime:               u64Ptr(1716998400),
+	GraniteTime:             u64Ptr(1723478400),
+	HoloceneTime:            u64Ptr(1732633200),
 	ProtocolVersionsAddress: common.HexToAddress("0x79ADD5713B383DAa0a138d3C4780C7A1804a8090"),
 }
 
@@ -126,22 +126,22 @@ var sepoliaDev0Cfg = rollup.Config{
 			GasLimit:    30000000,
 		},
 	},
-	BlockTime:              2,
-	MaxSequencerDrift:      600,
-	SeqWindowSize:          3600,
-	ChannelTimeoutBedrock:  300,
-	L1ChainID:              big.NewInt(11155111),
-	L2ChainID:              big.NewInt(11155421),
-	BatchInboxAddress:      common.HexToAddress("0xff00000000000000000000000000000011155421"),
-	DepositContractAddress: common.HexToAddress("0x76114bd29dFcC7a9892240D317E6c7C2A281Ffc6"),
-	L1SystemConfigAddress:  common.HexToAddress("0xa6b72407e2dc9EBF84b839B69A24C88929cf20F7"),
-	RegolithTime:           u64Ptr(0),
-	CanyonTime:             u64Ptr(0),
-	DeltaTime:              u64Ptr(0),
-	EcotoneTime:            u64Ptr(1706634000),
-	FjordTime:              u64Ptr(1715961600),
-	GraniteTime:            u64Ptr(1723046400),
-	// HoloceneTime: TBD
+	BlockTime:               2,
+	MaxSequencerDrift:       600,
+	SeqWindowSize:           3600,
+	ChannelTimeoutBedrock:   300,
+	L1ChainID:               big.NewInt(11155111),
+	L2ChainID:               big.NewInt(11155421),
+	BatchInboxAddress:       common.HexToAddress("0xff00000000000000000000000000000011155421"),
+	DepositContractAddress:  common.HexToAddress("0x76114bd29dFcC7a9892240D317E6c7C2A281Ffc6"),
+	L1SystemConfigAddress:   common.HexToAddress("0xa6b72407e2dc9EBF84b839B69A24C88929cf20F7"),
+	RegolithTime:            u64Ptr(0),
+	CanyonTime:              u64Ptr(0),
+	DeltaTime:               u64Ptr(0),
+	EcotoneTime:             u64Ptr(1706634000),
+	FjordTime:               u64Ptr(1715961600),
+	GraniteTime:             u64Ptr(1723046400),
+	HoloceneTime:            u64Ptr(1731682800),
 	ProtocolVersionsAddress: common.HexToAddress("0x252CbE9517F731C618961D890D534183822dcC8d"),
 }
 
diff --git a/op-node/rollup/superchain.go b/op-node/rollup/superchain.go
index e4ca043011a2..577a8dab7cea 100644
--- a/op-node/rollup/superchain.go
+++ b/op-node/rollup/superchain.go
@@ -12,7 +12,7 @@ import (
 	"github.com/ethereum-optimism/superchain-registry/superchain"
 )
 
-var OPStackSupport = params.ProtocolVersionV0{Build: [8]byte{}, Major: 8, Minor: 0, Patch: 0, PreRelease: 0}.Encode()
+var OPStackSupport = params.ProtocolVersionV0{Build: [8]byte{}, Major: 9, Minor: 0, Patch: 0, PreRelease: 1}.Encode()
 
 // LoadOPStackRollupConfig loads the rollup configuration of the requested chain ID from the superchain-registry.
 // Some chains may require a SystemConfigProvider to retrieve any values not part of the registry.

From 012398cd9287d468fcfcc6b18420597c623b1a7d Mon Sep 17 00:00:00 2001
From: Chen Kai <281165273grape@gmail.com>
Date: Thu, 14 Nov 2024 06:51:46 +0700
Subject: [PATCH 175/451] Feat: Add sll/srl/sra/sllv/srlv/srav/jr/jalr/bne/beq
 mips test (#12808)

* feat:add bne test

Signed-off-by: Chen Kai <281165273grape@gmail.com>

* feat:Add sll/srl/sra/sllv/srlv/srav/jr/jalr/beq/bne mips test

Signed-off-by: Chen Kai <281165273grape@gmail.com>

* Apply suggestions from code review

Co-authored-by: mbaxter 

* fix:add code review suggestion

Signed-off-by: Chen Kai <281165273grape@gmail.com>

---------

Signed-off-by: Chen Kai <281165273grape@gmail.com>
Co-authored-by: mbaxter 
---
 cannon/mipsevm/tests/evm_common_test.go | 133 ++++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go
index 6845fb07d59a..34a71b6a1b0d 100644
--- a/cannon/mipsevm/tests/evm_common_test.go
+++ b/cannon/mipsevm/tests/evm_common_test.go
@@ -413,6 +413,139 @@ func TestEVM_SingleStep_MthiMtlo(t *testing.T) {
 	}
 }
 
+func TestEVM_SingleStep_BeqBne(t *testing.T) {
+	versions := GetMipsVersionTestCases(t)
+	cases := []struct {
+		name   string
+		imm    uint32
+		opcode uint32
+		rs     Word
+		rt     Word
+	}{
+		{name: "bne", opcode: uint32(0x5), imm: uint32(0x10), rs: Word(0xaa), rt: Word(0xdeadbeef)},       // bne $t0, $t1, 16
+		{name: "beq", opcode: uint32(0x4), imm: uint32(0x10), rs: Word(0xdeadbeef), rt: Word(0xdeadbeef)}, // beq $t0, $t1, 16
+	}
+	for _, v := range versions {
+		for i, tt := range cases {
+			testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
+			t.Run(testName, func(t *testing.T) {
+				goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
+				state := goVm.GetState()
+				rsReg := uint32(9)
+				rtReg := uint32(8)
+				insn := tt.opcode<<26 | rsReg<<21 | rtReg<<16 | tt.imm
+				state.GetRegistersRef()[rtReg] = tt.rt
+				state.GetRegistersRef()[rsReg] = tt.rs
+				testutil.StoreInstruction(state.GetMemory(), 0, insn)
+				step := state.GetStep()
+				// Setup expectations
+				expected := testutil.NewExpectedState(state)
+				expected.Step = state.GetStep() + 1
+				expected.PC = state.GetCpu().NextPC
+				expected.NextPC = state.GetCpu().NextPC + Word(tt.imm<<2)
+
+				stepWitness, err := goVm.Step(true)
+				require.NoError(t, err)
+				// Check expectations
+				expected.Validate(t, state)
+				testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
+			})
+		}
+	}
+
+}
+
+func TestEVM_SingleStep_SlSr(t *testing.T) {
+	versions := GetMipsVersionTestCases(t)
+	cases := []struct {
+		name      string
+		rs        Word
+		rt        Word
+		rsReg     uint32
+		funct     uint16
+		expectVal Word
+	}{
+		{name: "sll", funct: uint16(4) << 6, rt: Word(0x20), rsReg: uint32(0x0), expectVal: Word(0x20) << uint8(4)},                         // sll t0, t1, 3
+		{name: "srl", funct: uint16(4)<<6 | 2, rt: Word(0x20), rsReg: uint32(0x0), expectVal: Word(0x20) >> uint8(4)},                       // srl t0, t1, 3
+		{name: "sra", funct: uint16(4)<<6 | 3, rt: Word(0x80_00_00_20), rsReg: uint32(0x0), expectVal: signExtend64(0xF8_00_00_02)},         // sra t0, t1, 3
+		{name: "sllv", funct: uint16(4), rt: Word(0x20), rs: Word(4), rsReg: uint32(0xa), expectVal: Word(0x20) << Word(4)},                 // sllv t0, t1, t2
+		{name: "srlv", funct: uint16(6), rt: Word(0x20_00), rs: Word(4), rsReg: uint32(0xa), expectVal: Word(0x20_00) >> Word(4)},           // srlv t0, t1, t2
+		{name: "srav", funct: uint16(7), rt: Word(0xdeafbeef), rs: Word(12), rsReg: uint32(0xa), expectVal: signExtend64(Word(0xfffdeafb))}, // srav t0, t1, t2
+	}
+
+	for _, v := range versions {
+		for i, tt := range cases {
+			testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
+			t.Run(testName, func(t *testing.T) {
+				goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
+				state := goVm.GetState()
+				var insn uint32
+				rtReg := uint32(0x9)
+				rdReg := uint32(0x8)
+				insn = tt.rsReg<<21 | rtReg<<16 | rdReg<<11 | uint32(tt.funct)
+				state.GetRegistersRef()[rtReg] = tt.rt
+				state.GetRegistersRef()[tt.rsReg] = tt.rs
+				testutil.StoreInstruction(state.GetMemory(), 0, insn)
+				step := state.GetStep()
+
+				// Setup expectations
+				expected := testutil.NewExpectedState(state)
+				expected.ExpectStep()
+
+				expected.Registers[rdReg] = tt.expectVal
+
+				stepWitness, err := goVm.Step(true)
+				require.NoError(t, err)
+
+				// Check expectations
+				expected.Validate(t, state)
+				testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
+			})
+		}
+	}
+}
+
+func TestEVM_SingleStep_JrJalr(t *testing.T) {
+	versions := GetMipsVersionTestCases(t)
+	cases := []struct {
+		name       string
+		funct      uint16
+		rdReg      uint32
+		expectLink bool
+	}{
+		{name: "jr", funct: uint16(0x8), rdReg: uint32(0)},                       // jr t0
+		{name: "jalr", funct: uint16(0x9), rdReg: uint32(0x9), expectLink: true}, // jalr t1, t0
+	}
+	for _, v := range versions {
+		for i, tt := range cases {
+			testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
+			t.Run(testName, func(t *testing.T) {
+				goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
+				state := goVm.GetState()
+				rsReg := uint32(8)
+				insn := rsReg<<21 | tt.rdReg<<11 | uint32(tt.funct)
+				state.GetRegistersRef()[rsReg] = Word(0x34)
+				testutil.StoreInstruction(state.GetMemory(), 0, insn)
+				step := state.GetStep()
+				// Setup expectations
+				expected := testutil.NewExpectedState(state)
+				expected.Step = state.GetStep() + 1
+				expected.PC = state.GetCpu().NextPC
+				expected.NextPC = state.GetRegistersRef()[rsReg]
+				if tt.expectLink {
+					expected.Registers[tt.rdReg] = state.GetPC() + 8
+				}
+
+				stepWitness, err := goVm.Step(true)
+				require.NoError(t, err)
+				// Check expectations
+				expected.Validate(t, state)
+				testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
+			})
+		}
+	}
+}
+
 func TestEVM_MMap(t *testing.T) {
 	versions := GetMipsVersionTestCases(t)
 	cases := []struct {

From fa9ffcff224d0d75f595e6db087e0f8277ca9905 Mon Sep 17 00:00:00 2001
From: Adrian Sutton 
Date: Thu, 14 Nov 2024 08:09:05 +0700
Subject: [PATCH 176/451] op-program: Add 1.4.0-rc.1 to list of op-program
 releases. (#12920)

---
 op-program/prestates/releases.json | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/op-program/prestates/releases.json b/op-program/prestates/releases.json
index 9345be39006b..63c54a0ba589 100644
--- a/op-program/prestates/releases.json
+++ b/op-program/prestates/releases.json
@@ -1,4 +1,8 @@
 [
+  {
+    "version": "1.4.0-rc.1",
+    "hash": "0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe"
+  },
   {
     "version": "1.3.1",
     "hash": "0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c",

From 2ef3a124794e27327b8c95f889724050c53363b9 Mon Sep 17 00:00:00 2001
From: Raffaele <151576068+raffaele-oplabs@users.noreply.github.com>
Date: Thu, 14 Nov 2024 15:41:44 +0100
Subject: [PATCH 177/451] removing unused action (#12927)

---
 .github/actions/setup/action.yml | 7 -------
 1 file changed, 7 deletions(-)
 delete mode 100644 .github/actions/setup/action.yml

diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
deleted file mode 100644
index 4c8918566485..000000000000
--- a/.github/actions/setup/action.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: Setup
-description: Common setup steps used by our workflows
-runs:
-  using: composite
-  steps:
-    - name: Setup foundry
-      uses: foundry-rs/foundry-toolchain@v1

From ae78b73dffbc2bb47ad10b812265db036a0797c3 Mon Sep 17 00:00:00 2001
From: mbaxter 
Date: Thu, 14 Nov 2024 10:19:43 -0500
Subject: [PATCH 178/451] cannon: Add differential tests for lui/clo/clz
 (#12906)

* cannon: Add differential tests for lui instruction

* cannon: Add clo, clz differential tests
---
 cannon/mipsevm/tests/evm_common_test.go | 87 +++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go
index 34a71b6a1b0d..cac15fb074e1 100644
--- a/cannon/mipsevm/tests/evm_common_test.go
+++ b/cannon/mipsevm/tests/evm_common_test.go
@@ -248,6 +248,93 @@ func TestEVM_SingleStep_LoadStore32(t *testing.T) {
 	testLoadStore(t, cases)
 }
 
+func TestEVM_SingleStep_Lui(t *testing.T) {
+	versions := GetMipsVersionTestCases(t)
+
+	cases := []struct {
+		name     string
+		rtReg    uint32
+		imm      uint32
+		expectRt Word
+	}{
+		{name: "lui unsigned", rtReg: 5, imm: 0x1234, expectRt: 0x1234_0000},
+		{name: "lui signed", rtReg: 7, imm: 0x8765, expectRt: signExtend64(0x8765_0000)},
+	}
+
+	for _, v := range versions {
+		for i, tt := range cases {
+			testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
+			t.Run(testName, func(t *testing.T) {
+				goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)))
+				state := goVm.GetState()
+				insn := 0b1111<<26 | uint32(tt.rtReg)<<16 | (tt.imm & 0xFFFF)
+				testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn)
+				step := state.GetStep()
+
+				// Setup expectations
+				expected := testutil.NewExpectedState(state)
+				expected.ExpectStep()
+				expected.Registers[tt.rtReg] = tt.expectRt
+				stepWitness, err := goVm.Step(true)
+				require.NoError(t, err)
+
+				// Check expectations
+				expected.Validate(t, state)
+				testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
+			})
+		}
+	}
+}
+
+func TestEVM_SingleStep_CloClz(t *testing.T) {
+	versions := GetMipsVersionTestCases(t)
+
+	rsReg := uint32(5)
+	rdReg := uint32(6)
+	cases := []struct {
+		name           string
+		rs             Word
+		expectedResult Word
+		funct          uint32
+	}{
+		{name: "clo", rs: 0xFFFF_FFFE, expectedResult: 31, funct: 0b10_0001},
+		{name: "clo", rs: 0xE000_0000, expectedResult: 3, funct: 0b10_0001},
+		{name: "clo", rs: 0x8000_0000, expectedResult: 1, funct: 0b10_0001},
+		{name: "clo, sign-extended", rs: signExtend64(0x8000_0000), expectedResult: 1, funct: 0b10_0001},
+		{name: "clo, sign-extended", rs: signExtend64(0xF800_0000), expectedResult: 5, funct: 0b10_0001},
+		{name: "clz", rs: 0x1, expectedResult: 31, funct: 0b10_0000},
+		{name: "clz", rs: 0x1000_0000, expectedResult: 3, funct: 0b10_0000},
+		{name: "clz", rs: 0x8000_0000, expectedResult: 0, funct: 0b10_0000},
+		{name: "clz, sign-extended", rs: signExtend64(0x8000_0000), expectedResult: 0, funct: 0b10_0000},
+	}
+
+	for _, v := range versions {
+		for i, tt := range cases {
+			testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
+			t.Run(testName, func(t *testing.T) {
+				// Set up state
+				goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)))
+				state := goVm.GetState()
+				insn := 0b01_1100<<26 | rsReg<<21 | rdReg<<11 | tt.funct
+				testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn)
+				state.GetRegistersRef()[rsReg] = tt.rs
+				step := state.GetStep()
+
+				// Setup expectations
+				expected := testutil.NewExpectedState(state)
+				expected.ExpectStep()
+				expected.Registers[rdReg] = tt.expectedResult
+				stepWitness, err := goVm.Step(true)
+				require.NoError(t, err)
+
+				// Check expectations
+				expected.Validate(t, state)
+				testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
+			})
+		}
+	}
+}
+
 func TestEVM_SingleStep_MovzMovn(t *testing.T) {
 	versions := GetMipsVersionTestCases(t)
 	cases := []struct {

From 50564d430460a4dc7b63787814bd322751660f4a Mon Sep 17 00:00:00 2001
From: Matthew Slipper 
Date: Thu, 14 Nov 2024 12:19:08 -0700
Subject: [PATCH 179/451] op-deployer: Support forking live chains (#12918)

---
 op-chain-ops/script/cheatcodes_forking.go     |  10 +-
 op-chain-ops/script/forking/db.go             |   4 +-
 op-chain-ops/script/forking/state.go          |  18 +-
 op-chain-ops/script/script.go                 |  13 +-
 op-chain-ops/script/script_test.go            |   4 +-
 op-chain-ops/script/with.go                   |   4 +-
 op-deployer/pkg/deployer/apply.go             | 222 ++++++++++-------
 .../pkg/deployer/bootstrap/delayed_weth.go    |   2 +-
 .../pkg/deployer/bootstrap/dispute_game.go    |   2 +-
 op-deployer/pkg/deployer/bootstrap/mips.go    |   2 +-
 op-deployer/pkg/deployer/bootstrap/opcm.go    |   2 +-
 op-deployer/pkg/deployer/inspect/semvers.go   |   1 -
 .../deployer/integration_test/apply_test.go   | 208 +++++-----------
 op-deployer/pkg/deployer/opcm/alt_da_test.go  |   1 -
 .../pkg/deployer/opcm/delayed_weth_test.go    |   1 -
 .../pkg/deployer/opcm/deployOutput_v160.json  |  77 ------
 .../pkg/deployer/opcm/dispute_game_test.go    |   1 -
 op-deployer/pkg/deployer/opcm/mips_test.go    |   1 -
 op-deployer/pkg/deployer/opcm/opchain.go      | 224 +++---------------
 op-deployer/pkg/deployer/pipeline/env.go      |   1 -
 .../pkg/deployer/pipeline/implementations.go  |   2 -
 .../pkg/deployer/pipeline/l2genesis.go        |  15 +-
 op-deployer/pkg/deployer/pipeline/opchain.go  | 214 +++--------------
 op-deployer/pkg/deployer/standard/standard.go |   2 +-
 op-deployer/pkg/env/host.go                   |  12 +-
 .../scripts/deploy/DeployOPChain.s.sol        |   2 +-
 .../deploy/ReadImplementationAddresses.s.sol  | 166 +++++++++++++
 .../scripts/ops/publish-artifacts.sh          |   9 +-
 28 files changed, 483 insertions(+), 737 deletions(-)
 delete mode 100644 op-deployer/pkg/deployer/opcm/deployOutput_v160.json
 create mode 100644 packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol

diff --git a/op-chain-ops/script/cheatcodes_forking.go b/op-chain-ops/script/cheatcodes_forking.go
index a1f30c7a11b9..44ba66f8a958 100644
--- a/op-chain-ops/script/cheatcodes_forking.go
+++ b/op-chain-ops/script/cheatcodes_forking.go
@@ -53,15 +53,7 @@ func (c *CheatCodesPrecompile) CreateSelectFork_84d52b7a(urlOrAlias string, txHa
 // createSelectFork implements vm.createSelectFork:
 // https://book.getfoundry.sh/cheatcodes/create-select-fork
 func (c *CheatCodesPrecompile) createSelectFork(opts ...ForkOption) (*big.Int, error) {
-	src, err := c.h.onFork(opts...)
-	if err != nil {
-		return nil, fmt.Errorf("failed to setup fork source: %w", err)
-	}
-	id, err := c.h.state.CreateSelectFork(src)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create-select fork: %w", err)
-	}
-	return id.U256().ToBig(), nil
+	return c.h.CreateSelectFork(opts...)
 }
 
 // ActiveFork implements vm.activeFork:
diff --git a/op-chain-ops/script/forking/db.go b/op-chain-ops/script/forking/db.go
index 6a40b4310d95..0a073500d247 100644
--- a/op-chain-ops/script/forking/db.go
+++ b/op-chain-ops/script/forking/db.go
@@ -53,7 +53,9 @@ func NewForkDB(source ForkSource) *ForkDB {
 // fakeRoot is just a marker; every account we load into the fork-db has this storage-root.
 // When opening a storage-trie, we sanity-check we have this root, or an empty trie.
 // And then just return the same global trie view for storage reads/writes.
-var fakeRoot = common.Hash{0: 42}
+// It needs to be set to EmptyRootHash to avoid contract collision errors when
+// deploying contracts, since Geth checks the storage root prior to deployment.
+var fakeRoot = types.EmptyRootHash
 
 func (f *ForkDB) OpenTrie(root common.Hash) (state.Trie, error) {
 	if f.active.stateRoot != root {
diff --git a/op-chain-ops/script/forking/state.go b/op-chain-ops/script/forking/state.go
index 5370179423ab..c3b6da1e3236 100644
--- a/op-chain-ops/script/forking/state.go
+++ b/op-chain-ops/script/forking/state.go
@@ -54,8 +54,6 @@ func NewForkableState(base VMStateDB) *ForkableState {
 			addresses.DefaultSenderAddr: ForkID{},
 			addresses.VMAddr:            ForkID{},
 			addresses.ConsoleAddr:       ForkID{},
-			addresses.ScriptDeployer:    ForkID{},
-			addresses.ForgeDeployer:     ForkID{},
 		},
 		fallback:  base,
 		idCounter: 0,
@@ -194,11 +192,27 @@ func (fst *ForkableState) MakePersistent(addr common.Address) {
 	fst.persistent[addr] = fst.activeFork
 }
 
+// MakeExcluded excludes an account from forking. This is useful for things like scripts, which
+// should always use the fallback state.
+func (fst *ForkableState) MakeExcluded(addr common.Address) {
+	fst.persistent[addr] = ForkID{}
+}
+
 // RevokePersistent is like vm.revokePersistent, it undoes a previous vm.makePersistent.
 func (fst *ForkableState) RevokePersistent(addr common.Address) {
 	delete(fst.persistent, addr)
 }
 
+// RevokeExcluded undoes MakeExcluded. It will panic if the account was marked as
+// persistent in a different fork.
+func (fst *ForkableState) RevokeExcluded(addr common.Address) {
+	forkID, ok := fst.persistent[addr]
+	if ok && forkID != (ForkID{}) {
+		panic(fmt.Sprintf("cannot revoke excluded account %s since it was made persistent in fork %q", addr, forkID))
+	}
+	delete(fst.persistent, addr)
+}
+
 // IsPersistent is like vm.isPersistent, it checks if an account persists across forks.
 func (fst *ForkableState) IsPersistent(addr common.Address) bool {
 	_, ok := fst.persistent[addr]
diff --git a/op-chain-ops/script/script.go b/op-chain-ops/script/script.go
index e5630d0783c5..3ce493487f77 100644
--- a/op-chain-ops/script/script.go
+++ b/op-chain-ops/script/script.go
@@ -9,7 +9,6 @@ import (
 	"math/big"
 
 	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
-
 	"github.com/holiman/uint256"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
@@ -850,3 +849,15 @@ func (h *Host) RememberOnLabel(label, srcFile, contract string) error {
 	})
 	return nil
 }
+
+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
+}
diff --git a/op-chain-ops/script/script_test.go b/op-chain-ops/script/script_test.go
index dbeef794fe37..48949347b63d 100644
--- a/op-chain-ops/script/script_test.go
+++ b/op-chain-ops/script/script_test.go
@@ -282,8 +282,8 @@ func TestForkingScript(t *testing.T) {
 	addr, err := h.LoadContract("ScriptExample.s.sol", "ForkTester")
 	require.NoError(t, err)
 	h.AllowCheatcodes(addr)
-	// Make this script persistent so it doesn't call the fork RPC.
-	h.state.MakePersistent(addr)
+	// Make this script excluded so it doesn't call the fork RPC.
+	h.state.MakeExcluded(addr)
 	t.Logf("allowing %s to access cheatcodes", addr)
 
 	input := bytes4("run()")
diff --git a/op-chain-ops/script/with.go b/op-chain-ops/script/with.go
index d70762a61f8e..0823bf9b7fbd 100644
--- a/op-chain-ops/script/with.go
+++ b/op-chain-ops/script/with.go
@@ -33,8 +33,8 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun
 	// compute address of script contract to be deployed
 	addr := crypto.CreateAddress(deployer, deployNonce)
 	h.Label(addr, contract)
-	h.AllowCheatcodes(addr)      // before constructor execution, give our script cheatcode access
-	h.state.MakePersistent(addr) // scripts are persistent across forks
+	h.AllowCheatcodes(addr)    // before constructor execution, give our script cheatcode access
+	h.state.MakeExcluded(addr) // scripts are persistent across forks
 
 	// init bindings (with ABI check)
 	bindings, err := MakeBindings[B](h.ScriptBackendFn(addr), func(abiDef string) bool {
diff --git a/op-deployer/pkg/deployer/apply.go b/op-deployer/pkg/deployer/apply.go
index bdfd5c55ea1e..f02716c655a5 100644
--- a/op-deployer/pkg/deployer/apply.go
+++ b/op-deployer/pkg/deployer/apply.go
@@ -7,11 +7,15 @@ import (
 	"math/big"
 	"strings"
 
+	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script"
+	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
+	"github.com/ethereum/go-ethereum/rpc"
+
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
 
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
 
-	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
 	"github.com/ethereum/go-ethereum/common"
 
@@ -104,50 +108,43 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
 		return fmt.Errorf("failed to read state: %w", err)
 	}
 
-	var l1Client *ethclient.Client
-	var deployer common.Address
-	var bcaster broadcaster.Broadcaster
-	var startingNonce uint64
-	if intent.DeploymentStrategy == state.DeploymentStrategyLive {
-		if err := cfg.CheckLive(); err != nil {
-			return fmt.Errorf("invalid config for apply: %w", err)
-		}
-
-		l1Client, err = ethclient.Dial(cfg.L1RPCUrl)
-		if err != nil {
-			return fmt.Errorf("failed to connect to L1 RPC: %w", err)
-		}
+	if err := ApplyPipeline(ctx, ApplyPipelineOpts{
+		L1RPCUrl:           cfg.L1RPCUrl,
+		DeployerPrivateKey: cfg.privateKeyECDSA,
+		Intent:             intent,
+		State:              st,
+		Logger:             cfg.Logger,
+		StateWriter:        pipeline.WorkdirStateWriter(cfg.Workdir),
+	}); err != nil {
+		return err
+	}
 
-		chainID, err := l1Client.ChainID(ctx)
-		if err != nil {
-			return fmt.Errorf("failed to get chain ID: %w", err)
-		}
+	return nil
+}
 
-		signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
-		deployer = crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)
+type pipelineStage struct {
+	name  string
+	apply func() error
+}
 
-		bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
-			Logger:  cfg.Logger,
-			ChainID: new(big.Int).SetUint64(intent.L1ChainID),
-			Client:  l1Client,
-			Signer:  signer,
-			From:    deployer,
-		})
-		if err != nil {
-			return fmt.Errorf("failed to create broadcaster: %w", err)
-		}
+type ApplyPipelineOpts struct {
+	L1RPCUrl           string
+	DeployerPrivateKey *ecdsa.PrivateKey
+	Intent             *state.Intent
+	State              *state.State
+	Logger             log.Logger
+	StateWriter        pipeline.StateWriter
+}
 
-		startingNonce, err = l1Client.NonceAt(ctx, deployer, nil)
-		if err != nil {
-			return fmt.Errorf("failed to get starting nonce: %w", err)
-		}
-	} else {
-		deployer = common.Address{0x01}
-		bcaster = broadcaster.NoopBroadcaster()
-	}
+func ApplyPipeline(
+	ctx context.Context,
+	opts ApplyPipelineOpts,
+) error {
+	intent := opts.Intent
+	st := opts.State
 
 	progressor := func(curr, total int64) {
-		cfg.Logger.Info("artifacts download progress", "current", curr, "total", total)
+		opts.Logger.Info("artifacts download progress", "current", curr, "total", total)
 	}
 
 	l1ArtifactsFS, cleanupL1, err := artifacts.Download(ctx, intent.L1ContractsLocator, progressor)
@@ -156,7 +153,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
 	}
 	defer func() {
 		if err := cleanupL1(); err != nil {
-			cfg.Logger.Warn("failed to clean up L1 artifacts", "err", err)
+			opts.Logger.Warn("failed to clean up L1 artifacts", "err", err)
 		}
 	}()
 
@@ -166,7 +163,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
 	}
 	defer func() {
 		if err := cleanupL2(); err != nil {
-			cfg.Logger.Warn("failed to clean up L2 artifacts", "err", err)
+			opts.Logger.Warn("failed to clean up L2 artifacts", "err", err)
 		}
 	}()
 
@@ -175,52 +172,101 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
 		L2: l2ArtifactsFS,
 	}
 
-	l1Host, err := env.DefaultScriptHost(bcaster, cfg.Logger, deployer, bundle.L1, startingNonce)
-	if err != nil {
-		return fmt.Errorf("failed to create L1 script host: %w", err)
+	var deployer common.Address
+	var bcaster broadcaster.Broadcaster
+	var l1Client *ethclient.Client
+	var l1Host *script.Host
+	if intent.DeploymentStrategy == state.DeploymentStrategyLive {
+		l1RPC, err := rpc.Dial(opts.L1RPCUrl)
+		if err != nil {
+			return fmt.Errorf("failed to connect to L1 RPC: %w", err)
+		}
+
+		l1Client = ethclient.NewClient(l1RPC)
+
+		chainID, err := l1Client.ChainID(ctx)
+		if err != nil {
+			return fmt.Errorf("failed to get chain ID: %w", err)
+		}
+
+		signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(opts.DeployerPrivateKey, chainID))
+		deployer = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey)
+
+		bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
+			Logger:  opts.Logger,
+			ChainID: new(big.Int).SetUint64(intent.L1ChainID),
+			Client:  l1Client,
+			Signer:  signer,
+			From:    deployer,
+		})
+		if err != nil {
+			return fmt.Errorf("failed to create broadcaster: %w", err)
+		}
+
+		l1Host, err = env.DefaultScriptHost(
+			bcaster,
+			opts.Logger,
+			deployer,
+			bundle.L1,
+			script.WithForkHook(func(cfg *script.ForkConfig) (forking.ForkSource, error) {
+				src, err := forking.RPCSourceByNumber(cfg.URLOrAlias, l1RPC, *cfg.BlockNumber)
+				if err != nil {
+					return nil, fmt.Errorf("failed to create RPC fork source: %w", err)
+				}
+				return forking.Cache(src), nil
+			}),
+		)
+		if err != nil {
+			return fmt.Errorf("failed to create L1 script host: %w", err)
+		}
+
+		latest, err := l1Client.HeaderByNumber(ctx, nil)
+		if err != nil {
+			return fmt.Errorf("failed to get latest block: %w", err)
+		}
+
+		if _, err := l1Host.CreateSelectFork(
+			script.ForkWithURLOrAlias("main"),
+			script.ForkWithBlockNumberU256(latest.Number),
+		); err != nil {
+			return fmt.Errorf("failed to select fork: %w", err)
+		}
+	} else {
+		deployer = common.Address{0x01}
+		bcaster = broadcaster.NoopBroadcaster()
+		l1Host, err = env.DefaultScriptHost(
+			bcaster,
+			opts.Logger,
+			deployer,
+			bundle.L1,
+		)
+		if err != nil {
+			return fmt.Errorf("failed to create L1 script host: %w", err)
+		}
 	}
 
-	env := &pipeline.Env{
-		StateWriter:  pipeline.WorkdirStateWriter(cfg.Workdir),
+	pEnv := &pipeline.Env{
+		StateWriter:  opts.StateWriter,
 		L1ScriptHost: l1Host,
 		L1Client:     l1Client,
-		Logger:       cfg.Logger,
+		Logger:       opts.Logger,
 		Broadcaster:  bcaster,
 		Deployer:     deployer,
 	}
 
-	if err := ApplyPipeline(ctx, env, bundle, intent, st); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-type pipelineStage struct {
-	name  string
-	apply func() error
-}
-
-func ApplyPipeline(
-	ctx context.Context,
-	env *pipeline.Env,
-	bundle pipeline.ArtifactsBundle,
-	intent *state.Intent,
-	st *state.State,
-) error {
 	pline := []pipelineStage{
 		{"init", func() error {
 			if intent.DeploymentStrategy == state.DeploymentStrategyLive {
-				return pipeline.InitLiveStrategy(ctx, env, intent, st)
+				return pipeline.InitLiveStrategy(ctx, pEnv, intent, st)
 			} else {
-				return pipeline.InitGenesisStrategy(env, intent, st)
+				return pipeline.InitGenesisStrategy(pEnv, intent, st)
 			}
 		}},
 		{"deploy-superchain", func() error {
-			return pipeline.DeploySuperchain(env, intent, st)
+			return pipeline.DeploySuperchain(pEnv, intent, st)
 		}},
 		{"deploy-implementations", func() error {
-			return pipeline.DeployImplementations(env, intent, st)
+			return pipeline.DeployImplementations(pEnv, intent, st)
 		}},
 	}
 
@@ -230,21 +276,17 @@ func ApplyPipeline(
 		pline = append(pline, pipelineStage{
 			fmt.Sprintf("deploy-opchain-%s", chainID.Hex()),
 			func() error {
-				if intent.DeploymentStrategy == state.DeploymentStrategyLive {
-					return pipeline.DeployOPChainLiveStrategy(ctx, env, bundle, intent, st, chainID)
-				} else {
-					return pipeline.DeployOPChainGenesisStrategy(env, intent, st, chainID)
-				}
+				return pipeline.DeployOPChain(pEnv, intent, st, chainID)
 			},
 		}, pipelineStage{
 			fmt.Sprintf("deploy-alt-da-%s", chainID.Hex()),
 			func() error {
-				return pipeline.DeployAltDA(env, intent, st, chainID)
+				return pipeline.DeployAltDA(pEnv, intent, st, chainID)
 			},
 		}, pipelineStage{
 			fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()),
 			func() error {
-				return pipeline.GenerateL2Genesis(env, intent, bundle, st, chainID)
+				return pipeline.GenerateL2Genesis(pEnv, intent, bundle, st, chainID)
 			},
 		})
 	}
@@ -257,9 +299,9 @@ func ApplyPipeline(
 			fmt.Sprintf("set-start-block-%s", chainID.Hex()),
 			func() error {
 				if intent.DeploymentStrategy == state.DeploymentStrategyLive {
-					return pipeline.SetStartBlockLiveStrategy(ctx, env, st, chainID)
+					return pipeline.SetStartBlockLiveStrategy(ctx, pEnv, st, chainID)
 				} else {
-					return pipeline.SetStartBlockGenesisStrategy(env, st, chainID)
+					return pipeline.SetStartBlockGenesisStrategy(pEnv, st, chainID)
 				}
 			},
 		})
@@ -271,23 +313,27 @@ func ApplyPipeline(
 		if err := stage.apply(); err != nil {
 			return fmt.Errorf("error in pipeline stage apply: %w", err)
 		}
-		dump, err := env.L1ScriptHost.StateDump()
-		if err != nil {
-			return fmt.Errorf("failed to dump state: %w", err)
-		}
-		st.L1StateDump = &state.GzipData[foundry.ForgeAllocs]{
-			Data: dump,
+
+		if intent.DeploymentStrategy == state.DeploymentStrategyGenesis {
+			dump, err := pEnv.L1ScriptHost.StateDump()
+			if err != nil {
+				return fmt.Errorf("failed to dump state: %w", err)
+			}
+			st.L1StateDump = &state.GzipData[foundry.ForgeAllocs]{
+				Data: dump,
+			}
 		}
-		if _, err := env.Broadcaster.Broadcast(ctx); err != nil {
+
+		if _, err := pEnv.Broadcaster.Broadcast(ctx); err != nil {
 			return fmt.Errorf("failed to broadcast stage %s: %w", stage.name, err)
 		}
-		if err := env.StateWriter.WriteState(st); err != nil {
+		if err := pEnv.StateWriter.WriteState(st); err != nil {
 			return fmt.Errorf("failed to write state: %w", err)
 		}
 	}
 
 	st.AppliedIntent = intent
-	if err := env.StateWriter.WriteState(st); err != nil {
+	if err := pEnv.StateWriter.WriteState(st); err != nil {
 		return fmt.Errorf("failed to write state: %w", err)
 	}
 
diff --git a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go
index 1e6e10535f87..451b0741f245 100644
--- a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go
+++ b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go
@@ -159,11 +159,11 @@ func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error {
 		lgr,
 		chainDeployer,
 		artifactsFS,
-		nonce,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create script host: %w", err)
 	}
+	host.SetNonce(chainDeployer, nonce)
 
 	var release string
 	if cfg.ArtifactsLocator.IsTag() {
diff --git a/op-deployer/pkg/deployer/bootstrap/dispute_game.go b/op-deployer/pkg/deployer/bootstrap/dispute_game.go
index 2b17a35be7bb..d3efe36f9ca8 100644
--- a/op-deployer/pkg/deployer/bootstrap/dispute_game.go
+++ b/op-deployer/pkg/deployer/bootstrap/dispute_game.go
@@ -162,11 +162,11 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error {
 		lgr,
 		chainDeployer,
 		artifactsFS,
-		nonce,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create script host: %w", err)
 	}
+	host.SetNonce(chainDeployer, nonce)
 
 	var release string
 	if cfg.ArtifactsLocator.IsTag() {
diff --git a/op-deployer/pkg/deployer/bootstrap/mips.go b/op-deployer/pkg/deployer/bootstrap/mips.go
index 42abae208eb5..efc1fd1c6e04 100644
--- a/op-deployer/pkg/deployer/bootstrap/mips.go
+++ b/op-deployer/pkg/deployer/bootstrap/mips.go
@@ -157,11 +157,11 @@ func MIPS(ctx context.Context, cfg MIPSConfig) error {
 		lgr,
 		chainDeployer,
 		artifactsFS,
-		nonce,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create script host: %w", err)
 	}
+	host.SetNonce(chainDeployer, nonce)
 
 	var release string
 	if cfg.ArtifactsLocator.IsTag() {
diff --git a/op-deployer/pkg/deployer/bootstrap/opcm.go b/op-deployer/pkg/deployer/bootstrap/opcm.go
index d584d6975f41..2f5976f304ad 100644
--- a/op-deployer/pkg/deployer/bootstrap/opcm.go
+++ b/op-deployer/pkg/deployer/bootstrap/opcm.go
@@ -193,11 +193,11 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
 		lgr,
 		chainDeployer,
 		artifactsFS,
-		nonce,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create script host: %w", err)
 	}
+	host.SetNonce(chainDeployer, nonce)
 
 	var release string
 	if cfg.ArtifactsLocator.IsTag() {
diff --git a/op-deployer/pkg/deployer/inspect/semvers.go b/op-deployer/pkg/deployer/inspect/semvers.go
index 7c8e4ce9f3dd..da666096ee18 100644
--- a/op-deployer/pkg/deployer/inspect/semvers.go
+++ b/op-deployer/pkg/deployer/inspect/semvers.go
@@ -72,7 +72,6 @@ func L2SemversCLI(cliCtx *cli.Context) error {
 		l,
 		common.Address{19: 0x01},
 		artifactsFS,
-		0,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create script host: %w", err)
diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go
index fa59c49dc8ac..215687c5cfc0 100644
--- a/op-deployer/pkg/deployer/integration_test/apply_test.go
+++ b/op-deployer/pkg/deployer/integration_test/apply_test.go
@@ -3,6 +3,7 @@ package integration_test
 import (
 	"bytes"
 	"context"
+	"crypto/rand"
 	"encoding/hex"
 	"fmt"
 	"log/slog"
@@ -17,19 +18,13 @@ import (
 
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
 
-	"github.com/ethereum-optimism/optimism/op-chain-ops/script"
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
-
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
-	"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
-	"github.com/ethereum/go-ethereum/crypto"
-	"github.com/ethereum/go-ethereum/log"
-
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
+	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
+	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
+	"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
+	"github.com/ethereum/go-ethereum/crypto"
 
 	op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
 
@@ -37,7 +32,6 @@ import (
 
 	"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
 	"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
-	opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
 	"github.com/ethereum-optimism/optimism/op-service/predeploys"
 	"github.com/ethereum-optimism/optimism/op-service/testlog"
 	"github.com/ethereum-optimism/optimism/op-service/testutils/kurtosisutil"
@@ -106,36 +100,25 @@ func TestEndToEndApply(t *testing.T) {
 	require.NoError(t, err)
 	pk, err := dk.Secret(depKey)
 	require.NoError(t, err)
-	signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID))
 
 	l2ChainID1 := uint256.NewInt(1)
 	l2ChainID2 := uint256.NewInt(2)
 
-	deployerAddr, err := dk.Address(depKey)
-	require.NoError(t, err)
-
 	loc, _ := testutil.LocalArtifacts(t)
-
-	bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
-		Logger:  log.NewLogger(log.DiscardHandler()),
-		ChainID: l1ChainID,
-		Client:  l1Client,
-		Signer:  signer,
-		From:    deployerAddr,
-	})
-	require.NoError(t, err)
-
-	env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr)
 	intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
 	cg := ethClientCodeGetter(ctx, l1Client)
 
 	t.Run("initial chain", func(t *testing.T) {
 		require.NoError(t, deployer.ApplyPipeline(
 			ctx,
-			env,
-			bundle,
-			intent,
-			st,
+			deployer.ApplyPipelineOpts{
+				L1RPCUrl:           rpcURL,
+				DeployerPrivateKey: pk,
+				Intent:             intent,
+				State:              st,
+				Logger:             lgr,
+				StateWriter:        pipeline.NoopStateWriter(),
+			},
 		))
 
 		validateSuperchainDeployment(t, st, cg)
@@ -145,15 +128,18 @@ func TestEndToEndApply(t *testing.T) {
 	t.Run("subsequent chain", func(t *testing.T) {
 		// create a new environment with wiped state to ensure we can continue using the
 		// state from the previous deployment
-		env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr)
 		intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2))
 
 		require.NoError(t, deployer.ApplyPipeline(
 			ctx,
-			env,
-			bundle,
-			intent,
-			st,
+			deployer.ApplyPipelineOpts{
+				L1RPCUrl:           rpcURL,
+				DeployerPrivateKey: pk,
+				Intent:             intent,
+				State:              st,
+				Logger:             lgr,
+				StateWriter:        pipeline.NoopStateWriter(),
+			},
 		))
 
 		validateOPChainDeployment(t, cg, st, intent)
@@ -191,24 +177,11 @@ func TestApplyExistingOPCM(t *testing.T) {
 	dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
 	require.NoError(t, err)
 	// index 0 from Anvil's test set
-	priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
+	pk, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
 	require.NoError(t, err)
-	signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID))
-	deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
 
 	l2ChainID := uint256.NewInt(1)
 
-	bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
-		Logger:  lgr,
-		ChainID: l1ChainID,
-		Client:  l1Client,
-		Signer:  signer,
-		From:    deployerAddr,
-	})
-	require.NoError(t, err)
-
-	env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr)
-
 	intent, st := newIntent(
 		t,
 		l1ChainID,
@@ -217,13 +190,20 @@ func TestApplyExistingOPCM(t *testing.T) {
 		artifacts.DefaultL1ContractsLocator,
 		artifacts.DefaultL2ContractsLocator,
 	)
+	// Define a new create2 salt to avoid contract address collisions
+	_, err = rand.Read(st.Create2Salt[:])
+	require.NoError(t, err)
 
 	require.NoError(t, deployer.ApplyPipeline(
 		ctx,
-		env,
-		bundle,
-		intent,
-		st,
+		deployer.ApplyPipelineOpts{
+			L1RPCUrl:           runner.RPCUrl(),
+			DeployerPrivateKey: pk,
+			Intent:             intent,
+			State:              st,
+			Logger:             lgr,
+			StateWriter:        pipeline.NoopStateWriter(),
+		},
 	))
 
 	validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent)
@@ -236,18 +216,12 @@ func TestL2BlockTimeOverride(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	env, bundle, intent, st := setupGenesisChain(t)
+	opts, intent, st := setupGenesisChain(t)
 	intent.GlobalDeployOverrides = map[string]interface{}{
 		"l2BlockTime": float64(3),
 	}
 
-	require.NoError(t, deployer.ApplyPipeline(
-		ctx,
-		env,
-		bundle,
-		intent,
-		st,
-	))
+	require.NoError(t, deployer.ApplyPipeline(ctx, opts))
 
 	cfg, err := state.CombineDeployConfig(intent, intent.Chains[0], st, st.Chains[0])
 	require.NoError(t, err)
@@ -260,16 +234,9 @@ func TestApplyGenesisStrategy(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	env, bundle, intent, st := setupGenesisChain(t)
-	intent.DeploymentStrategy = state.DeploymentStrategyGenesis
+	opts, intent, st := setupGenesisChain(t)
 
-	require.NoError(t, deployer.ApplyPipeline(
-		ctx,
-		env,
-		bundle,
-		intent,
-		st,
-	))
+	require.NoError(t, deployer.ApplyPipeline(ctx, opts))
 
 	cg := stateDumpCodeGetter(st)
 	validateSuperchainDeployment(t, st, cg)
@@ -287,7 +254,7 @@ func TestProofParamOverrides(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	env, bundle, intent, st := setupGenesisChain(t)
+	opts, intent, st := setupGenesisChain(t)
 	intent.GlobalDeployOverrides = map[string]any{
 		"withdrawalDelaySeconds":                  standard.WithdrawalDelaySeconds + 1,
 		"minProposalSizeBytes":                    standard.MinProposalSizeBytes + 1,
@@ -304,13 +271,7 @@ func TestProofParamOverrides(t *testing.T) {
 		"dangerouslyAllowCustomDisputeParameters": true,
 	}
 
-	require.NoError(t, deployer.ApplyPipeline(
-		ctx,
-		env,
-		bundle,
-		intent,
-		st,
-	))
+	require.NoError(t, deployer.ApplyPipeline(ctx, opts))
 
 	allocs := st.L1StateDump.Data.Accounts
 	chainState := st.Chains[0]
@@ -390,16 +351,10 @@ func TestInteropDeployment(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	env, bundle, intent, st := setupGenesisChain(t)
+	opts, intent, st := setupGenesisChain(t)
 	intent.UseInterop = true
 
-	require.NoError(t, deployer.ApplyPipeline(
-		ctx,
-		env,
-		bundle,
-		intent,
-		st,
-	))
+	require.NoError(t, deployer.ApplyPipeline(ctx, opts))
 
 	chainState := st.Chains[0]
 	depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c")
@@ -414,7 +369,7 @@ func TestAltDADeployment(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	env, bundle, intent, st := setupGenesisChain(t)
+	opts, intent, st := setupGenesisChain(t)
 	altDACfg := genesis.AltDADeployConfig{
 		UseAltDA:                   true,
 		DACommitmentType:           altda.KeccakCommitmentString,
@@ -425,13 +380,7 @@ func TestAltDADeployment(t *testing.T) {
 	}
 	intent.Chains[0].DangerousAltDAConfig = altDACfg
 
-	require.NoError(t, deployer.ApplyPipeline(
-		ctx,
-		env,
-		bundle,
-		intent,
-		st,
-	))
+	require.NoError(t, deployer.ApplyPipeline(ctx, opts))
 
 	chainState := st.Chains[0]
 	require.NotEmpty(t, chainState.DataAvailabilityChallengeProxyAddress)
@@ -450,23 +399,9 @@ func TestAltDADeployment(t *testing.T) {
 func TestInvalidL2Genesis(t *testing.T) {
 	op_e2e.InitParallel(t)
 
-	lgr := testlog.Logger(t, slog.LevelDebug)
-
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	depKey := new(deployerKey)
-	l1ChainID := big.NewInt(77799777)
-	dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
-	require.NoError(t, err)
-
-	l2ChainID1 := uint256.NewInt(1)
-
-	deployerAddr, err := dk.Address(depKey)
-	require.NoError(t, err)
-
-	loc, _ := testutil.LocalArtifacts(t)
-
 	// these tests were generated by grepping all usages of the deploy
 	// config in L2Genesis.s.sol.
 	tests := []struct {
@@ -512,26 +447,18 @@ func TestInvalidL2Genesis(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
-			intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
-			intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
+			opts, intent, _ := setupGenesisChain(t)
 			intent.DeploymentStrategy = state.DeploymentStrategyGenesis
 			intent.GlobalDeployOverrides = tt.overrides
 
-			err := deployer.ApplyPipeline(
-				ctx,
-				env,
-				bundle,
-				intent,
-				st,
-			)
+			err := deployer.ApplyPipeline(ctx, opts)
 			require.Error(t, err)
 			require.ErrorContains(t, err, "failed to combine L2 init config")
 		})
 	}
 }
 
-func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *state.Intent, *state.State) {
+func setupGenesisChain(t *testing.T) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) {
 	lgr := testlog.Logger(t, slog.LevelDebug)
 
 	depKey := new(deployerKey)
@@ -541,51 +468,24 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *
 
 	l2ChainID1 := uint256.NewInt(1)
 
-	deployerAddr, err := dk.Address(depKey)
+	priv, err := dk.Secret(depKey)
 	require.NoError(t, err)
 
 	loc, _ := testutil.LocalArtifacts(t)
 
-	env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
 	intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
 	intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
 	intent.DeploymentStrategy = state.DeploymentStrategyGenesis
-	return env, bundle, intent, st
-}
-
-func createEnv(
-	t *testing.T,
-	lgr log.Logger,
-	l1Client *ethclient.Client,
-	bcaster broadcaster.Broadcaster,
-	deployerAddr common.Address,
-) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) {
-	_, artifactsFS := testutil.LocalArtifacts(t)
-
-	host, err := env.DefaultScriptHost(
-		bcaster,
-		lgr,
-		deployerAddr,
-		artifactsFS,
-		0,
-	)
-	require.NoError(t, err)
-
-	env := &pipeline.Env{
-		StateWriter:  pipeline.NoopStateWriter(),
-		L1ScriptHost: host,
-		L1Client:     l1Client,
-		Broadcaster:  bcaster,
-		Deployer:     deployerAddr,
-		Logger:       lgr,
-	}
 
-	bundle := pipeline.ArtifactsBundle{
-		L1: artifactsFS,
-		L2: artifactsFS,
+	opts := deployer.ApplyPipelineOpts{
+		DeployerPrivateKey: priv,
+		Intent:             intent,
+		State:              st,
+		Logger:             lgr,
+		StateWriter:        pipeline.NoopStateWriter(),
 	}
 
-	return env, bundle, host
+	return opts, intent, st
 }
 
 func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address {
diff --git a/op-deployer/pkg/deployer/opcm/alt_da_test.go b/op-deployer/pkg/deployer/opcm/alt_da_test.go
index 20b93907ebea..97e2b485b900 100644
--- a/op-deployer/pkg/deployer/opcm/alt_da_test.go
+++ b/op-deployer/pkg/deployer/opcm/alt_da_test.go
@@ -21,7 +21,6 @@ func TestDeployAltDA(t *testing.T) {
 		testlog.Logger(t, log.LevelInfo),
 		common.Address{'D'},
 		artifacts,
-		0,
 	)
 	require.NoError(t, err)
 
diff --git a/op-deployer/pkg/deployer/opcm/delayed_weth_test.go b/op-deployer/pkg/deployer/opcm/delayed_weth_test.go
index 98ca7ab6996b..071afe89fdbe 100644
--- a/op-deployer/pkg/deployer/opcm/delayed_weth_test.go
+++ b/op-deployer/pkg/deployer/opcm/delayed_weth_test.go
@@ -22,7 +22,6 @@ func TestDeployDelayedWETH(t *testing.T) {
 		testlog.Logger(t, log.LevelInfo),
 		common.Address{'D'},
 		artifacts,
-		0,
 	)
 	require.NoError(t, err)
 
diff --git a/op-deployer/pkg/deployer/opcm/deployOutput_v160.json b/op-deployer/pkg/deployer/opcm/deployOutput_v160.json
deleted file mode 100644
index 888e28a102f8..000000000000
--- a/op-deployer/pkg/deployer/opcm/deployOutput_v160.json
+++ /dev/null
@@ -1,77 +0,0 @@
-[
-  {
-    "type": "function",
-    "name": "decodeOutput",
-    "inputs": [],
-    "outputs": [
-      {
-        "name": "output",
-        "indexed": false,
-        "type": "tuple",
-        "components": [
-          {
-            "name": "opChainProxyAdmin",
-            "type": "address"
-          },
-          {
-            "name": "addressManager",
-            "type": "address"
-          },
-          {
-            "name": "l1ERC721BridgeProxy",
-            "type": "address"
-          },
-          {
-            "name": "systemConfigProxy",
-            "type": "address"
-          },
-          {
-            "name": "optimismMintableERC20FactoryProxy",
-            "type": "address"
-          },
-          {
-            "name": "l1StandardBridgeProxy",
-            "type": "address"
-          },
-          {
-            "name": "l1CrossDomainMessengerProxy",
-            "type": "address"
-          },
-          {
-            "name": "optimismPortalProxy",
-            "type": "address"
-          },
-          {
-            "name": "disputeGameFactoryProxy",
-            "type": "address"
-          },
-          {
-            "name": "anchorStateRegistryProxy",
-            "type": "address"
-          },
-          {
-            "name": "anchorStateRegistryImpl",
-            "type": "address"
-          },
-          {
-            "name": "faultDisputeGame",
-            "type": "address",
-            "internalType": "contract FaultDisputeGame"
-          },
-          {
-            "name": "permissionedDisputeGame",
-            "type": "address"
-          },
-          {
-            "name": "delayedWETHPermissionedGameProxy",
-            "type": "address"
-          },
-          {
-            "name": "delayedWETHPermissionlessGameProxy",
-            "type": "address"
-          }
-        ]
-      }
-    ]
-  }
-]
\ No newline at end of file
diff --git a/op-deployer/pkg/deployer/opcm/dispute_game_test.go b/op-deployer/pkg/deployer/opcm/dispute_game_test.go
index d31f94f8d83b..f39849be4403 100644
--- a/op-deployer/pkg/deployer/opcm/dispute_game_test.go
+++ b/op-deployer/pkg/deployer/opcm/dispute_game_test.go
@@ -21,7 +21,6 @@ func TestDeployDisputeGame(t *testing.T) {
 		testlog.Logger(t, log.LevelInfo),
 		common.Address{'D'},
 		artifacts,
-		0,
 	)
 	require.NoError(t, err)
 
diff --git a/op-deployer/pkg/deployer/opcm/mips_test.go b/op-deployer/pkg/deployer/opcm/mips_test.go
index 04f8d93ae9bd..848b46356525 100644
--- a/op-deployer/pkg/deployer/opcm/mips_test.go
+++ b/op-deployer/pkg/deployer/opcm/mips_test.go
@@ -20,7 +20,6 @@ func TestDeployMIPS(t *testing.T) {
 		testlog.Logger(t, log.LevelInfo),
 		common.Address{'D'},
 		artifacts,
-		0,
 	)
 	require.NoError(t, err)
 
diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go
index 537765ce5154..1e1b468a417f 100644
--- a/op-deployer/pkg/deployer/opcm/opchain.go
+++ b/op-deployer/pkg/deployer/opcm/opchain.go
@@ -1,22 +1,12 @@
 package opcm
 
 import (
-	"context"
+	_ "embed"
 	"fmt"
 	"math/big"
-	"strings"
-
-	_ "embed"
 
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
-
-	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
 	"github.com/ethereum-optimism/optimism/op-chain-ops/script"
-	"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/ethclient"
-	"github.com/holiman/uint256"
 )
 
 // PermissionedGameStartingAnchorRoots is a root of bytes32(hex"dead") for the permissioned game at block 0,
@@ -25,48 +15,6 @@ var PermissionedGameStartingAnchorRoots = []byte{
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 }
 
-// opcmRolesBase is an internal struct used to pass the roles to OPCM. See opcmDeployInputV160 for more info.
-type opcmRolesBase struct {
-	OpChainProxyAdminOwner common.Address
-	SystemConfigOwner      common.Address
-	Batcher                common.Address
-	UnsafeBlockSigner      common.Address
-	Proposer               common.Address
-	Challenger             common.Address
-}
-
-type opcmDeployInputBase struct {
-	BasefeeScalar           uint32
-	BlobBasefeeScalar       uint32
-	L2ChainId               *big.Int
-	StartingAnchorRoots     []byte
-	SaltMixer               string
-	GasLimit                uint64
-	DisputeGameType         uint32
-	DisputeAbsolutePrestate common.Hash
-	DisputeMaxGameDepth     *big.Int
-	DisputeSplitDepth       *big.Int
-	DisputeClockExtension   uint64
-	DisputeMaxClockDuration uint64
-}
-
-// opcmDeployInputV160 is the input struct for the deploy method of the OPStackManager contract. We
-// define a separate struct here to match what the OPSM contract expects.
-type opcmDeployInputV160 struct {
-	opcmDeployInputBase
-	Roles opcmRolesBase
-}
-
-type opcmRolesIsthmus struct {
-	opcmRolesBase
-	FeeAdmin common.Address
-}
-
-type opcmDeployInputIsthmus struct {
-	opcmDeployInputBase
-	Roles opcmRolesIsthmus
-}
-
 type DeployOPChainInputV160 struct {
 	OpChainProxyAdminOwner common.Address
 	SystemConfigOwner      common.Address
@@ -99,49 +47,11 @@ func (input *DeployOPChainInputV160) StartingAnchorRoots() []byte {
 	return PermissionedGameStartingAnchorRoots
 }
 
-func DeployOPChainInputV160DeployCalldata(input DeployOPChainInputV160) any {
-	return opcmDeployInputV160{
-		Roles: opcmRolesBase{
-			OpChainProxyAdminOwner: input.OpChainProxyAdminOwner,
-			SystemConfigOwner:      input.SystemConfigOwner,
-			Batcher:                input.Batcher,
-			UnsafeBlockSigner:      input.UnsafeBlockSigner,
-			Proposer:               input.Proposer,
-			Challenger:             input.Challenger,
-		},
-		opcmDeployInputBase: opcmDeployInputBase{
-			BasefeeScalar:           input.BasefeeScalar,
-			BlobBasefeeScalar:       input.BlobBaseFeeScalar,
-			L2ChainId:               input.L2ChainId,
-			StartingAnchorRoots:     input.StartingAnchorRoots(),
-			SaltMixer:               input.SaltMixer,
-			GasLimit:                input.GasLimit,
-			DisputeGameType:         input.DisputeGameType,
-			DisputeAbsolutePrestate: input.DisputeAbsolutePrestate,
-			DisputeMaxGameDepth:     new(big.Int).SetUint64(input.DisputeMaxGameDepth),
-			DisputeSplitDepth:       new(big.Int).SetUint64(input.DisputeSplitDepth),
-			DisputeClockExtension:   input.DisputeClockExtension,
-			DisputeMaxClockDuration: input.DisputeMaxClockDuration,
-		},
-	}
-}
-
 type DeployOPChainInputIsthmus struct {
 	DeployOPChainInputV160
 	SystemConfigFeeAdmin common.Address
 }
 
-func DeployOPChainInputIsthmusDeployCalldata(input DeployOPChainInputIsthmus) any {
-	v160Data := DeployOPChainInputV160DeployCalldata(input.DeployOPChainInputV160).(opcmDeployInputV160)
-	return opcmDeployInputIsthmus{
-		Roles: opcmRolesIsthmus{
-			opcmRolesBase: v160Data.Roles,
-			FeeAdmin:      input.SystemConfigFeeAdmin,
-		},
-		opcmDeployInputBase: v160Data.opcmDeployInputBase,
-	}
-}
-
 type DeployOPChainOutput struct {
 	OpChainProxyAdmin                 common.Address
 	AddressManager                    common.Address
@@ -210,116 +120,58 @@ func deployOPChain[T any](host *script.Host, input T) (DeployOPChainOutput, erro
 	return dco, nil
 }
 
-// decodeOutputABIJSONV160 defines an ABI for a fake method called "decodeOutput" that returns the
-// DeployOutput struct. This allows the code in the deployer to decode directly into a struct
-// using Geth's ABI library.
-//
-//go:embed deployOutput_v160.json
-var decodeOutputABIJSONV160 string
-
-var decodeOutputABIV160 abi.ABI
-
-func DeployOPChainRawV160(
-	ctx context.Context,
-	l1 *ethclient.Client,
-	bcast broadcaster.Broadcaster,
-	deployer common.Address,
-	artifacts foundry.StatDirFs,
-	input DeployOPChainInputV160,
-) (DeployOPChainOutput, error) {
-	return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputV160DeployCalldata(input))
+type ReadImplementationAddressesInput struct {
+	DeployOPChainOutput
+	OpcmProxy common.Address
+	Release   string
 }
 
-func DeployOPChainRawIsthmus(
-	ctx context.Context,
-	l1 *ethclient.Client,
-	bcast broadcaster.Broadcaster,
-	deployer common.Address,
-	artifacts foundry.StatDirFs,
-	input DeployOPChainInputIsthmus,
-) (DeployOPChainOutput, error) {
-	return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputIsthmusDeployCalldata(input))
+type ReadImplementationAddressesOutput struct {
+	DelayedWETH                  common.Address
+	OptimismPortal               common.Address
+	SystemConfig                 common.Address
+	L1CrossDomainMessenger       common.Address
+	L1ERC721Bridge               common.Address
+	L1StandardBridge             common.Address
+	OptimismMintableERC20Factory common.Address
+	DisputeGameFactory           common.Address
+	MipsSingleton                common.Address
+	PreimageOracleSingleton      common.Address
 }
 
-// DeployOPChainRaw deploys an OP Chain using a raw call to a pre-deployed OPSM contract.
-func deployOPChainRaw(
-	ctx context.Context,
-	l1 *ethclient.Client,
-	bcast broadcaster.Broadcaster,
-	deployer common.Address,
-	artifacts foundry.StatDirFs,
-	opcmProxyAddress common.Address,
-	input any,
-) (DeployOPChainOutput, error) {
-	var out DeployOPChainOutput
+type ReadImplementationAddressesScript struct {
+	Run func(input, output common.Address) error
+}
 
-	artifactsFS := &foundry.ArtifactsFS{FS: artifacts}
-	opcmArtifacts, err := artifactsFS.ReadArtifact("OPContractsManager.sol", "OPContractsManager")
-	if err != nil {
-		return out, fmt.Errorf("failed to read OPStackManager artifact: %w", err)
-	}
+func ReadImplementationAddresses(host *script.Host, input ReadImplementationAddressesInput) (ReadImplementationAddressesOutput, error) {
+	var rio ReadImplementationAddressesOutput
+	inputAddr := host.NewScriptAddress()
+	outputAddr := host.NewScriptAddress()
 
-	opcmABI := opcmArtifacts.ABI
-	calldata, err := opcmABI.Pack("deploy", input)
+	cleanupInput, err := script.WithPrecompileAtAddress[*ReadImplementationAddressesInput](host, inputAddr, &input)
 	if err != nil {
-		return out, fmt.Errorf("failed to pack deploy input: %w", err)
+		return rio, fmt.Errorf("failed to insert ReadImplementationAddressesInput precompile: %w", err)
 	}
+	defer cleanupInput()
+	host.Label(inputAddr, "ReadImplementationAddressesInput")
 
-	nonce, err := l1.NonceAt(ctx, deployer, nil)
+	cleanupOutput, err := script.WithPrecompileAtAddress[*ReadImplementationAddressesOutput](host, outputAddr, &rio,
+		script.WithFieldSetter[*ReadImplementationAddressesOutput])
 	if err != nil {
-		return out, fmt.Errorf("failed to read nonce: %w", err)
+		return rio, fmt.Errorf("failed to insert ReadImplementationAddressesOutput precompile: %w", err)
 	}
+	defer cleanupOutput()
+	host.Label(outputAddr, "ReadImplementationAddressesOutput")
 
-	bcast.Hook(script.Broadcast{
-		From:  deployer,
-		To:    opcmProxyAddress,
-		Input: calldata,
-		Value: (*hexutil.U256)(uint256.NewInt(0)),
-		// use hardcoded 19MM gas for now since this is roughly what we've seen this deployment cost.
-		GasUsed: 19_000_000,
-		Type:    script.BroadcastCall,
-		Nonce:   nonce,
-	})
-
-	results, err := bcast.Broadcast(ctx)
+	deployScript, cleanupDeploy, err := script.WithScript[ReadImplementationAddressesScript](host, "ReadImplementationAddresses.s.sol", "ReadImplementationAddresses")
 	if err != nil {
-		return out, fmt.Errorf("failed to broadcast OP chain deployment: %w", err)
+		return rio, fmt.Errorf("failed to load ReadImplementationAddresses script: %w", err)
 	}
+	defer cleanupDeploy()
 
-	deployedEvent := opcmABI.Events["Deployed"]
-	res := results[0]
-
-	for _, log := range res.Receipt.Logs {
-		if log.Topics[0] != deployedEvent.ID {
-			continue
-		}
-
-		type EventData struct {
-			DeployOutput []byte
-		}
-		var data EventData
-		if err := opcmABI.UnpackIntoInterface(&data, "Deployed", log.Data); err != nil {
-			return out, fmt.Errorf("failed to unpack Deployed event: %w", err)
-		}
-
-		type OutputData struct {
-			Output DeployOPChainOutput
-		}
-		var outData OutputData
-		if err := decodeOutputABIV160.UnpackIntoInterface(&outData, "decodeOutput", data.DeployOutput); err != nil {
-			return out, fmt.Errorf("failed to unpack DeployOutput: %w", err)
-		}
-
-		return outData.Output, nil
+	if err := deployScript.Run(inputAddr, outputAddr); err != nil {
+		return rio, fmt.Errorf("failed to run ReadImplementationAddresses script: %w", err)
 	}
 
-	return out, fmt.Errorf("failed to find Deployed event")
-}
-
-func init() {
-	var err error
-	decodeOutputABIV160, err = abi.JSON(strings.NewReader(decodeOutputABIJSONV160))
-	if err != nil {
-		panic(fmt.Sprintf("failed to parse decodeOutput ABI: %v", err))
-	}
+	return rio, nil
 }
diff --git a/op-deployer/pkg/deployer/pipeline/env.go b/op-deployer/pkg/deployer/pipeline/env.go
index 0b6ca536dd8b..20d8daaf76fa 100644
--- a/op-deployer/pkg/deployer/pipeline/env.go
+++ b/op-deployer/pkg/deployer/pipeline/env.go
@@ -23,7 +23,6 @@ type Env struct {
 	L1ScriptHost *script.Host
 	L1Client     *ethclient.Client
 	Broadcaster  broadcaster.Broadcaster
-	Host         *script.Host
 	Deployer     common.Address
 	Logger       log.Logger
 }
diff --git a/op-deployer/pkg/deployer/pipeline/implementations.go b/op-deployer/pkg/deployer/pipeline/implementations.go
index 9da1d4a900a5..47ea91fbe963 100644
--- a/op-deployer/pkg/deployer/pipeline/implementations.go
+++ b/op-deployer/pkg/deployer/pipeline/implementations.go
@@ -58,8 +58,6 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro
 		return fmt.Errorf("error merging proof params from overrides: %w", err)
 	}
 
-	env.L1ScriptHost.ImportState(st.L1StateDump.Data)
-
 	dio, err := opcm.DeployImplementations(
 		env.L1ScriptHost,
 		opcm.DeployImplementationsInput{
diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis.go b/op-deployer/pkg/deployer/pipeline/l2genesis.go
index a5dabefd8e84..945cb5af8ff0 100644
--- a/op-deployer/pkg/deployer/pipeline/l2genesis.go
+++ b/op-deployer/pkg/deployer/pipeline/l2genesis.go
@@ -3,7 +3,7 @@ package pipeline
 import (
 	"fmt"
 
-	env2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
+	"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
 
 	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
@@ -13,8 +13,8 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 )
 
-func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, st *state.State, chainID common.Hash) error {
-	lgr := env.Logger.New("stage", "generate-l2-genesis")
+func GenerateL2Genesis(pEnv *Env, intent *state.Intent, bundle ArtifactsBundle, st *state.State, chainID common.Hash) error {
+	lgr := pEnv.Logger.New("stage", "generate-l2-genesis")
 
 	thisIntent, err := intent.Chain(chainID)
 	if err != nil {
@@ -38,12 +38,11 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s
 		return fmt.Errorf("failed to combine L2 init config: %w", err)
 	}
 
-	host, err := env2.DefaultScriptHost(
+	host, err := env.DefaultScriptHost(
 		broadcaster.NoopBroadcaster(),
-		env.Logger,
-		env.Deployer,
+		pEnv.Logger,
+		pEnv.Deployer,
 		bundle.L2,
-		0,
 	)
 	if err != nil {
 		return fmt.Errorf("failed to create L2 script host: %w", err)
@@ -60,7 +59,7 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s
 		return fmt.Errorf("failed to call L2Genesis script: %w", err)
 	}
 
-	host.Wipe(env.Deployer)
+	host.Wipe(pEnv.Deployer)
 
 	dump, err := host.StateDump()
 	if err != nil {
diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go
index 59e5d214cf4f..a49848883cea 100644
--- a/op-deployer/pkg/deployer/pipeline/opchain.go
+++ b/op-deployer/pkg/deployer/pipeline/opchain.go
@@ -1,25 +1,18 @@
 package pipeline
 
 import (
-	"context"
-	"encoding/hex"
-	"errors"
 	"fmt"
 
-	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
-
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
 	"github.com/ethereum-optimism/optimism/op-service/jsonutil"
 
-	"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
 	"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
 	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/ethclient"
 )
 
-func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State, chainID common.Hash) error {
-	lgr := env.Logger.New("stage", "deploy-opchain", "strategy", "live")
+func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error {
+	lgr := env.Logger.New("stage", "deploy-opchain")
 
 	if !shouldDeployOPChain(st, chainID) {
 		lgr.Info("opchain deployment not needed")
@@ -31,6 +24,7 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
 		return fmt.Errorf("failed to get chain intent: %w", err)
 	}
 
+	var opcmAddr common.Address
 	var deployFunc func() (opcm.DeployOPChainOutput, error)
 	switch intent.L1ContractsLocator.Tag {
 	case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag:
@@ -40,13 +34,8 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
 				return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
 			}
 
-			return opcm.DeployOPChainRawV160(ctx,
-				env.L1Client,
-				env.Broadcaster,
-				env.Deployer,
-				bundle.L1,
-				input,
-			)
+			opcmAddr = input.OpcmProxy
+			return opcm.DeployOPChainV160(env.L1ScriptHost, input)
 		}
 	default:
 		deployFunc = func() (opcm.DeployOPChainOutput, error) {
@@ -55,165 +44,47 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
 				return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
 			}
 
-			return opcm.DeployOPChainRawIsthmus(ctx,
-				env.L1Client,
-				env.Broadcaster,
-				env.Deployer,
-				bundle.L1,
-				input,
-			)
+			opcmAddr = input.OpcmProxy
+			return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input)
 		}
 	}
 
 	var dco opcm.DeployOPChainOutput
-	lgr.Info("deploying OP chain using existing OPCM", "id", chainID.Hex(), "opcmAddress", st.ImplementationsDeployment.OpcmProxyAddress.Hex())
+	lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
 	dco, err = deployFunc()
 	if err != nil {
 		return fmt.Errorf("error deploying OP chain: %w", err)
 	}
 
 	st.Chains = append(st.Chains, makeChainState(chainID, dco))
-	opcmProxyAddress := st.ImplementationsDeployment.OpcmProxyAddress
-	err = conditionallySetImplementationAddresses(ctx, env.L1Client, intent, st, dco, opcmProxyAddress)
-	if err != nil {
-		return fmt.Errorf("failed to set implementation addresses: %w", err)
-	}
-
-	return nil
-}
-
-// Only try to set the implementation addresses if we reused existing implementations from a release tag.
-// The reason why these addresses could be empty is because only DeployOPChain.s.sol is invoked as part of the pipeline.
-func conditionallySetImplementationAddresses(ctx context.Context, client *ethclient.Client, intent *state.Intent, st *state.State, dco opcm.DeployOPChainOutput, opcmProxyAddress common.Address) error {
-	if !intent.L1ContractsLocator.IsTag() {
-		return nil
-	}
-
-	block, err := client.BlockByNumber(ctx, nil)
-	if err != nil {
-		return fmt.Errorf("failed to get latest block by number: %w", err)
-	}
-	currentBlockHash := block.Hash()
-
-	errCh := make(chan error, 8)
-
-	setImplementationAddressTasks := []func(){
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.DelayedWETHPermissionedGameProxy, currentBlockHash, &st.ImplementationsDeployment.DelayedWETHImplAddress)
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismPortalProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismPortalImplAddress)
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.SystemConfigProxy, currentBlockHash, &st.ImplementationsDeployment.SystemConfigImplAddress)
-		},
-		func() {
-			setRDPImplementationAddress(ctx, client, errCh, dco.AddressManager, &st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress, "OVM_L1CrossDomainMessenger")
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1ERC721BridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1ERC721BridgeImplAddress)
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1StandardBridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1StandardBridgeImplAddress)
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismMintableERC20FactoryProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress)
-		},
-		func() {
-			setEIP1967ImplementationAddress(ctx, client, errCh, dco.DisputeGameFactoryProxy, currentBlockHash, &st.ImplementationsDeployment.DisputeGameFactoryImplAddress)
-		},
-		func() {
-			setMipsSingletonAddress(ctx, client, intent.L1ContractsLocator, errCh, opcmProxyAddress, &st.ImplementationsDeployment.MipsSingletonAddress)
-			setPreimageOracleAddress(ctx, client, errCh, st.ImplementationsDeployment.MipsSingletonAddress, &st.ImplementationsDeployment.PreimageOracleSingletonAddress)
-		},
-	}
-
-	for _, task := range setImplementationAddressTasks {
-		go task()
-	}
-
-	var lastTaskErr error
-	for i := 0; i < len(setImplementationAddressTasks); i++ {
-		taskErr := <-errCh
-		if taskErr != nil {
-			lastTaskErr = taskErr
-		}
-	}
-	if lastTaskErr != nil {
-		return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr)
-	}
-
-	return nil
-}
-
-func setMipsSingletonAddress(ctx context.Context, client *ethclient.Client, l1ArtifactsLocator *artifacts.Locator, errCh chan error, opcmProxyAddress common.Address, singletonAddress *common.Address) {
-	if !l1ArtifactsLocator.IsTag() {
-		errCh <- errors.New("L1 contracts locator is not a tag, cannot set MIPS singleton address")
-		return
-	}
-	opcmContract := opcm.NewContract(opcmProxyAddress, client)
-	mipsSingletonAddress, err := opcmContract.GetOPCMImplementationAddress(ctx, l1ArtifactsLocator.Tag, "MIPS")
-
-	if err == nil {
-		*singletonAddress = mipsSingletonAddress
-	}
-	errCh <- err
-}
-
-func setPreimageOracleAddress(ctx context.Context, client *ethclient.Client, errCh chan error, mipsSingletonAddress common.Address, preimageOracleAddress *common.Address) {
-	opcmContract := opcm.NewContract(mipsSingletonAddress, client)
-	preimageOracle, err := opcmContract.GenericAddressGetter(ctx, "oracle")
-	if err == nil {
-		*preimageOracleAddress = preimageOracle
-	}
-	errCh <- err
-}
-
-func DeployOPChainGenesisStrategy(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error {
-	lgr := env.Logger.New("stage", "deploy-opchain", "strategy", "genesis")
 
-	if !shouldDeployOPChain(st, chainID) {
-		lgr.Info("opchain deployment not needed")
-		return nil
+	var release string
+	if intent.L1ContractsLocator.IsTag() {
+		release = intent.L1ContractsLocator.Tag
+	} else {
+		release = "dev"
 	}
 
-	thisIntent, err := intent.Chain(chainID)
-	if err != nil {
-		return fmt.Errorf("failed to get chain intent: %w", err)
+	readInput := opcm.ReadImplementationAddressesInput{
+		DeployOPChainOutput: dco,
+		OpcmProxy:           opcmAddr,
+		Release:             release,
 	}
-
-	var deployFunc func() (opcm.DeployOPChainOutput, error)
-	switch intent.L1ContractsLocator.Tag {
-	case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag:
-		deployFunc = func() (opcm.DeployOPChainOutput, error) {
-			input, err := makeDCIV160(intent, thisIntent, chainID, st)
-			if err != nil {
-				return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
-			}
-
-			return opcm.DeployOPChainV160(env.L1ScriptHost, input)
-		}
-	default:
-		deployFunc = func() (opcm.DeployOPChainOutput, error) {
-			input, err := makeDCIIsthmus(intent, thisIntent, chainID, st)
-			if err != nil {
-				return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
-			}
-
-			return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input)
-		}
-	}
-
-	env.L1ScriptHost.ImportState(st.L1StateDump.Data)
-
-	var dco opcm.DeployOPChainOutput
-	lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
-	dco, err = deployFunc()
+	impls, err := opcm.ReadImplementationAddresses(env.L1ScriptHost, readInput)
 	if err != nil {
-		return fmt.Errorf("error deploying OP chain: %w", err)
+		return fmt.Errorf("failed to read implementation addresses: %w", err)
 	}
 
-	st.Chains = append(st.Chains, makeChainState(chainID, dco))
+	st.ImplementationsDeployment.DelayedWETHImplAddress = impls.DelayedWETH
+	st.ImplementationsDeployment.OptimismPortalImplAddress = impls.OptimismPortal
+	st.ImplementationsDeployment.SystemConfigImplAddress = impls.SystemConfig
+	st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress = impls.L1CrossDomainMessenger
+	st.ImplementationsDeployment.L1ERC721BridgeImplAddress = impls.L1ERC721Bridge
+	st.ImplementationsDeployment.L1StandardBridgeImplAddress = impls.L1StandardBridge
+	st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress = impls.OptimismMintableERC20Factory
+	st.ImplementationsDeployment.DisputeGameFactoryImplAddress = impls.DisputeGameFactory
+	st.ImplementationsDeployment.MipsSingletonAddress = impls.MipsSingleton
+	st.ImplementationsDeployment.PreimageOracleSingletonAddress = impls.PreimageOracleSingleton
 
 	return nil
 }
@@ -301,33 +172,6 @@ func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.Ch
 	}
 }
 
-func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address, getNameArg string) {
-	if *implAddress != (common.Address{}) {
-		errCh <- nil
-		return
-	}
-
-	addressManagerContract := opcm.NewContract(addressManager, client)
-	address, err := addressManagerContract.GetAddressByNameViaAddressManager(ctx, getNameArg)
-	if err == nil {
-		*implAddress = address
-	}
-	errCh <- err
-}
-
-func setEIP1967ImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, proxy common.Address, currentBlockHash common.Hash, implAddress *common.Address) {
-	if *implAddress != (common.Address{}) {
-		errCh <- nil
-		return
-	}
-
-	storageValue, err := client.StorageAtHash(ctx, proxy, genesis.ImplementationSlot, currentBlockHash)
-	if err == nil {
-		*implAddress = common.HexToAddress(hex.EncodeToString(storageValue))
-	}
-	errCh <- err
-}
-
 func shouldDeployOPChain(st *state.State, chainID common.Hash) bool {
 	for _, chain := range st.Chains {
 		if chain.ID == chainID {
diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go
index 78144ca1a7bf..2fb6de8c5bf7 100644
--- a/op-deployer/pkg/deployer/standard/standard.go
+++ b/op-deployer/pkg/deployer/standard/standard.go
@@ -172,7 +172,7 @@ func SystemOwnerAddrFor(chainID uint64) (common.Address, error) {
 func ArtifactsURLForTag(tag string) (*url.URL, error) {
 	switch tag {
 	case "op-contracts/v1.6.0":
-		return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f"))
+		return url.Parse(standardArtifactsURL("3a27c6dc0cb61b36feaac26def98c64b4a48ec8f5c5ba6965e8ae3157606043c"))
 	case "op-contracts/v1.7.0-beta.1+l2-contracts":
 		return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597"))
 	default:
diff --git a/op-deployer/pkg/env/host.go b/op-deployer/pkg/env/host.go
index 04a0c4a172d6..61d30b4ce253 100644
--- a/op-deployer/pkg/env/host.go
+++ b/op-deployer/pkg/env/host.go
@@ -16,7 +16,7 @@ func DefaultScriptHost(
 	lgr log.Logger,
 	deployer common.Address,
 	artifacts foundry.StatDirFs,
-	startingNonce uint64,
+	additionalOpts ...script.HostOption,
 ) (*script.Host, error) {
 	scriptCtx := script.DefaultContext
 	scriptCtx.Sender = deployer
@@ -26,16 +26,16 @@ func DefaultScriptHost(
 		&foundry.ArtifactsFS{FS: artifacts},
 		nil,
 		scriptCtx,
-		script.WithBroadcastHook(bcaster.Hook),
-		script.WithIsolatedBroadcasts(),
-		script.WithCreate2Deployer(),
+		append([]script.HostOption{
+			script.WithBroadcastHook(bcaster.Hook),
+			script.WithIsolatedBroadcasts(),
+			script.WithCreate2Deployer(),
+		}, additionalOpts...)...,
 	)
 
 	if err := h.EnableCheats(); err != nil {
 		return nil, fmt.Errorf("failed to enable cheats: %w", err)
 	}
 
-	h.SetNonce(deployer, startingNonce)
-
 	return h, nil
 }
diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol
index 7a5d1f5cc6b2..eb3b346452e9 100644
--- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol
+++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol
@@ -235,7 +235,7 @@ contract DeployOPChainOutput is BaseDeployIO {
     IDelayedWETH internal _delayedWETHPermissionedGameProxy;
     IDelayedWETH internal _delayedWETHPermissionlessGameProxy;
 
-    function set(bytes4 _sel, address _addr) public {
+    function set(bytes4 _sel, address _addr) public virtual {
         require(_addr != address(0), "DeployOPChainOutput: cannot set zero address");
         // forgefmt: disable-start
         if (_sel == this.opChainProxyAdmin.selector) _opChainProxyAdmin = IProxyAdmin(_addr) ;
diff --git a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol
new file mode 100644
index 000000000000..3992cf1cb657
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.15;
+
+import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol";
+import { IProxy } from "src/universal/interfaces/IProxy.sol";
+import { Script } from "forge-std/Script.sol";
+import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
+import { DeployOPChainOutput } from "scripts/deploy/DeployOPChain.s.sol";
+import { IMIPS } from "src/cannon/interfaces/IMIPS.sol";
+import { OPContractsManager } from "src/L1/OPContractsManager.sol";
+import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol";
+import { IStaticL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol";
+
+contract ReadImplementationAddressesInput is DeployOPChainOutput {
+    OPContractsManager internal _opcmProxy;
+    string internal _release;
+
+    function set(bytes4 _sel, address _addr) public override {
+        require(_addr != address(0), "ReadImplementationAddressesInput: cannot set zero address");
+        if (_sel == this.opcmProxy.selector) _opcmProxy = OPContractsManager(_addr);
+        else if (_sel == this.addressManager.selector) _addressManager = IAddressManager(_addr);
+        else super.set(_sel, _addr);
+    }
+
+    function set(bytes4 _sel, string memory _val) public {
+        if (_sel == this.release.selector) _release = _val;
+        else revert("ReadImplementationAddressesInput: unknown selector");
+    }
+
+    function opcmProxy() public view returns (OPContractsManager) {
+        DeployUtils.assertValidContractAddress(address(_opcmProxy));
+        return _opcmProxy;
+    }
+
+    function release() public view returns (string memory) {
+        require(bytes(_release).length != 0, "ReadImplementationAddressesInput: release not set");
+        return _release;
+    }
+}
+
+contract ReadImplementationAddressesOutput is BaseDeployIO {
+    address internal _delayedWETH;
+    address internal _optimismPortal;
+    address internal _systemConfig;
+    address internal _l1CrossDomainMessenger;
+    address internal _l1ERC721Bridge;
+    address internal _l1StandardBridge;
+    address internal _optimismMintableERC20Factory;
+    address internal _disputeGameFactory;
+    address internal _mipsSingleton;
+    address internal _preimageOracleSingleton;
+
+    function set(bytes4 _sel, address _addr) public {
+        require(_addr != address(0), "ReadImplementationAddressesOutput: cannot set zero address");
+        if (_sel == this.delayedWETH.selector) _delayedWETH = _addr;
+        else if (_sel == this.optimismPortal.selector) _optimismPortal = _addr;
+        else if (_sel == this.systemConfig.selector) _systemConfig = _addr;
+        else if (_sel == this.l1CrossDomainMessenger.selector) _l1CrossDomainMessenger = _addr;
+        else if (_sel == this.l1ERC721Bridge.selector) _l1ERC721Bridge = _addr;
+        else if (_sel == this.l1StandardBridge.selector) _l1StandardBridge = _addr;
+        else if (_sel == this.optimismMintableERC20Factory.selector) _optimismMintableERC20Factory = _addr;
+        else if (_sel == this.disputeGameFactory.selector) _disputeGameFactory = _addr;
+        else if (_sel == this.mipsSingleton.selector) _mipsSingleton = _addr;
+        else if (_sel == this.preimageOracleSingleton.selector) _preimageOracleSingleton = _addr;
+        else revert("ReadImplementationAddressesOutput: unknown selector");
+    }
+
+    function delayedWETH() public view returns (address) {
+        require(_delayedWETH != address(0), "ReadImplementationAddressesOutput: delayedWETH not set");
+        return _delayedWETH;
+    }
+
+    function optimismPortal() public view returns (address) {
+        require(_optimismPortal != address(0), "ReadImplementationAddressesOutput: optimismPortal not set");
+        return _optimismPortal;
+    }
+
+    function systemConfig() public view returns (address) {
+        require(_systemConfig != address(0), "ReadImplementationAddressesOutput: systemConfig not set");
+        return _systemConfig;
+    }
+
+    function l1CrossDomainMessenger() public view returns (address) {
+        require(
+            _l1CrossDomainMessenger != address(0), "ReadImplementationAddressesOutput: l1CrossDomainMessenger not set"
+        );
+        return _l1CrossDomainMessenger;
+    }
+
+    function l1ERC721Bridge() public view returns (address) {
+        require(_l1ERC721Bridge != address(0), "ReadImplementationAddressesOutput: l1ERC721Bridge not set");
+        return _l1ERC721Bridge;
+    }
+
+    function l1StandardBridge() public view returns (address) {
+        require(_l1StandardBridge != address(0), "ReadImplementationAddressesOutput: l1StandardBridge not set");
+        return _l1StandardBridge;
+    }
+
+    function optimismMintableERC20Factory() public view returns (address) {
+        require(
+            _optimismMintableERC20Factory != address(0),
+            "ReadImplementationAddressesOutput: optimismMintableERC20Factory not set"
+        );
+        return _optimismMintableERC20Factory;
+    }
+
+    function disputeGameFactory() public view returns (address) {
+        require(_disputeGameFactory != address(0), "ReadImplementationAddressesOutput: disputeGameFactory not set");
+        return _disputeGameFactory;
+    }
+
+    function mipsSingleton() public view returns (address) {
+        require(_mipsSingleton != address(0), "ReadImplementationAddressesOutput: mipsSingleton not set");
+        return _mipsSingleton;
+    }
+
+    function preimageOracleSingleton() public view returns (address) {
+        require(
+            _preimageOracleSingleton != address(0), "ReadImplementationAddressesOutput: preimageOracleSingleton not set"
+        );
+        return _preimageOracleSingleton;
+    }
+}
+
+contract ReadImplementationAddresses is Script {
+    function run(ReadImplementationAddressesInput _rii, ReadImplementationAddressesOutput _rio) public {
+        address[6] memory eip1967Proxies = [
+            address(_rii.delayedWETHPermissionedGameProxy()),
+            address(_rii.optimismPortalProxy()),
+            address(_rii.systemConfigProxy()),
+            address(_rii.l1ERC721BridgeProxy()),
+            address(_rii.optimismMintableERC20FactoryProxy()),
+            address(_rii.disputeGameFactoryProxy())
+        ];
+
+        bytes4[6] memory sels = [
+            _rio.delayedWETH.selector,
+            _rio.optimismPortal.selector,
+            _rio.systemConfig.selector,
+            _rio.l1ERC721Bridge.selector,
+            _rio.optimismMintableERC20Factory.selector,
+            _rio.disputeGameFactory.selector
+        ];
+
+        for (uint256 i = 0; i < eip1967Proxies.length; i++) {
+            IProxy proxy = IProxy(payable(eip1967Proxies[i]));
+            vm.prank(address(0));
+            _rio.set(sels[i], proxy.implementation());
+        }
+
+        vm.prank(address(0));
+        address l1SBImpl = IStaticL1ChugSplashProxy(address(_rii.l1StandardBridgeProxy())).getImplementation();
+        vm.prank(address(0));
+        _rio.set(_rio.l1StandardBridge.selector, l1SBImpl);
+
+        (address mipsLogic,) = _rii.opcmProxy().implementations(_rii.release(), "MIPS");
+        _rio.set(_rio.mipsSingleton.selector, mipsLogic);
+
+        IAddressManager am = _rii.addressManager();
+        _rio.set(_rio.l1CrossDomainMessenger.selector, am.getAddress("OVM_L1CrossDomainMessenger"));
+
+        address preimageOracle = address(IMIPS(mipsLogic).oracle());
+        _rio.set(_rio.preimageOracleSingleton.selector, preimageOracle);
+    }
+}
diff --git a/packages/contracts-bedrock/scripts/ops/publish-artifacts.sh b/packages/contracts-bedrock/scripts/ops/publish-artifacts.sh
index 720e71ad9720..9e04ea8d0fdc 100644
--- a/packages/contracts-bedrock/scripts/ops/publish-artifacts.sh
+++ b/packages/contracts-bedrock/scripts/ops/publish-artifacts.sh
@@ -44,7 +44,11 @@ else
   tar="tar"
 fi
 
-"$tar" -czf "$archive_name" artifacts forge-artifacts cache
+rm -f COMMIT
+commit=$(git rev-parse HEAD)
+echo "$commit" > COMMIT
+
+"$tar" -czf "$archive_name" artifacts forge-artifacts cache COMMIT
 du -sh "$archive_name" | awk '{$1=$1};1' # trim leading whitespace
 echoerr "> Done."
 
@@ -52,4 +56,5 @@ echoerr "> Uploading artifacts to GCS..."
 gcloud storage cp "$archive_name" "gs://$DEPLOY_BUCKET/$archive_name"
 echoerr "> Done."
 
-rm "$archive_name"
\ No newline at end of file
+rm "$archive_name"
+rm COMMIT
\ No newline at end of file

From 2fecdde0d8ee5ef549a80ff47c679961dc82e4f9 Mon Sep 17 00:00:00 2001
From: clabby 
Date: Fri, 15 Nov 2024 00:21:03 -0500
Subject: [PATCH 180/451] feat(ctb): Holocene upgrade package (#12878)

* feat(ctb): Holocene upgrade package

* lint

* lint

* add example env to sc-ops task

* fix

* reuse `SystemConfig` and `MIPS` impls optionally

* optionally deploy a new `DelayedWETH` proxy + impl

add check

* env lint

* add docker image to docker tag service

* kelvin idiot-proofing ask

* fetch standard config addresses for Holocene

* lint

* ajsutton review: Deploy separate DelayedWETH proxies for the FDG/PDG

* ajsutton review: Conditional FDG usage

* use contracts rc tag

* mds review

* split `SystemConfig` and proofs contracts upgrades

* add option to completely disclude the fault proof contracts

fix

* Update README

* verify

* deploy config

* fix contract tag

* README update

* semgrep
---
 docker-bake.hcl                               |  11 +
 ops/scripts/ci-docker-tag-op-stack-release.sh |   2 +-
 .../deploy-config/sepolia-devnet-0.json       |   2 +-
 .../scripts/upgrades/holocene/.env.example    |  93 +++++
 .../scripts/upgrades/holocene/.gitignore      |   2 +
 .../upgrades/holocene/DeployUpgrade.s.sol     | 337 ++++++++++++++++++
 .../scripts/upgrades/holocene/README.md       |  41 +++
 .../scripts/upgrades/holocene/justfile        |  26 ++
 .../upgrades/holocene/scripts/common.sh       | 131 +++++++
 .../upgrades/holocene/scripts/deploy.sh       |  46 +++
 .../scripts/upgrades/holocene/scripts/main.sh | 152 ++++++++
 .../holocene/scripts/proofs-bundle.sh         |  54 +++
 .../holocene/scripts/sc-ops-proofs.sh         |  47 +++
 .../holocene/scripts/sc-ops-sys-cfg.sh        |  38 ++
 .../holocene/scripts/sys-cfg-bundle.sh        |  35 ++
 .../templates/fdg_bundle_extension.json       |  29 ++
 .../proof_upgrade_bundle_template.json        |  38 ++
 .../templates/proofs-sc-ops-task/.env.example |   6 +
 .../templates/proofs-sc-ops-task/README.md    |  46 +++
 .../proofs-sc-ops-task/VALIDATION.md          |  24 ++
 .../sys-cfg-sc-ops-task/.env.example          |   6 +
 .../templates/sys-cfg-sc-ops-task/README.md   |  42 +++
 .../sys-cfg-sc-ops-task/VALIDATION.md         |  19 +
 .../sys_cfg_upgrade_bundle_template.json      |  38 ++
 .../upgrades/holocene/upgrade.dockerfile      |  62 ++++
 25 files changed, 1325 insertions(+), 2 deletions(-)
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/.env.example
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/README.md
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/justfile
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh
 create mode 100755 packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json
 create mode 100644 packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile

diff --git a/docker-bake.hcl b/docker-bake.hcl
index 5f7f311cefe4..b53c7add0f87 100644
--- a/docker-bake.hcl
+++ b/docker-bake.hcl
@@ -214,6 +214,17 @@ target "proofs-tools" {
   tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/proofs-tools:${tag}"]
 }
 
+target "holocene-deployer" {
+  dockerfile = "./packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile"
+  context = "./packages/contracts-bedrock/scripts/upgrades/holocene"
+  args = {
+    REV = "op-contracts/v1.8.0-rc.1"
+  }
+  target="holocene-deployer"
+  platforms = split(",", PLATFORMS)
+  tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/holocene-deployer:${tag}"]
+}
+
 target "ci-builder" {
   dockerfile = "./ops/docker/ci-builder/Dockerfile"
   context = "."
diff --git a/ops/scripts/ci-docker-tag-op-stack-release.sh b/ops/scripts/ci-docker-tag-op-stack-release.sh
index 45dd92094994..1de86b749d32 100755
--- a/ops/scripts/ci-docker-tag-op-stack-release.sh
+++ b/ops/scripts/ci-docker-tag-op-stack-release.sh
@@ -6,7 +6,7 @@ DOCKER_REPO=$1
 GIT_TAG=$2
 GIT_SHA=$3
 
-IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true)
+IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|holocene-deployer|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true)
 if [ -z "$IMAGE_NAME" ]; then
   echo "image name could not be parsed from git tag '$GIT_TAG'"
   exit 1
diff --git a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json
index ff87fc4f8cbb..4239d10c3d07 100644
--- a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json
+++ b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json
@@ -65,7 +65,7 @@
   "eip1559Denominator": 250,
   "eip1559DenominatorCanyon": 250,
   "systemConfigStartBlock": 4071248,
-  "faultGameAbsolutePrestate": "0x0385c3f8ee78491001d92b90b07d0cf387b7b52ab9b83b4d87c994e92cf823ba",
+  "faultGameAbsolutePrestate": "0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe",
   "faultGameMaxDepth": 73,
   "faultGameClockExtension": 3600,
   "faultGameMaxClockDuration": 14400,
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example
new file mode 100644
index 000000000000..02d67a4bad06
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example
@@ -0,0 +1,93 @@
+##############################################
+#               ↓  Required  ↓               #
+##############################################
+
+# The network to deploy the contracts to.
+# Must be one of 'mainnet', 'sepolia'
+NETWORK=
+
+# Etherscan API key used to verify contract bytecode
+ETHERSCAN_API_KEY=
+
+# RPC URL for the L1 network that matches $NETWORK
+ETH_RPC_URL=
+
+# Private key used to deploy the new contracts for this upgrade
+PRIVATE_KEY=
+
+# Path to the deploy config JSON file
+DEPLOY_CONFIG_PATH=
+
+# Path to the folder where output artifacts will be stored
+OUTPUT_FOLDER_PATH=
+
+# Address of deployed `PreimageOracle` contract.
+PREIMAGE_ORACLE_ADDR=
+
+# Address of deployed `AnchorStateRegistry` proxy contract.
+ANCHOR_STATE_REGISTRY_PROXY_ADDR=
+
+# Address of the `SuperchainConfig` proxy contract.
+SUPERCHAIN_CONFIG_PROXY_ADDR=
+
+# Address of deployed `ProxyAdmin` contract.
+PROXY_ADMIN_ADDR=
+
+# Address of deployed `SystemConfig` proxy contract.
+SYSTEM_CONFIG_PROXY_ADDR=
+
+# Address of deployed `DisputeGameFactory` proxy contract.
+DISPUTE_GAME_FACTORY_PROXY_ADDR=
+
+# Whether or not to deploy and include any fault proof contracts in the upgrade.
+#
+# If 'true', the `PermissionedDisputeGame` contract will be deployed and included in the upgrade.
+# If 'false', the `PermissionedDisputeGame` contract will not be deployed or included in the upgrade.
+#
+# Must be one of 'true', 'false'
+# Cannot be 'false' if `USE_PERMISSIONLESS_FAULT_PROOFS` is 'true'
+USE_FAULT_PROOFS=true
+
+# Whether or not to deploy and include the `FaultDisputeGame` contract in the upgrade.
+#
+# If 'true', the `FaultDisputeGame` contract will be deployed and included in the upgrade.
+# If 'false', the `FaultDisputeGame` contract will not be deployed or included in the upgrade.
+#
+# Must be one of 'true', 'false'
+# Cannot be 'true' if `USE_FAULT_PROOFS` is 'false'
+USE_PERMISSIONLESS_FAULT_PROOFS=true
+
+###################################################
+#                 ↓  Optional  ↓                  #
+# Do not set if you don't know what you're doing. #
+###################################################
+
+# Address of the deployed `SystemConfig` implementation for Holocene.
+#
+# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`.
+# If you are not the first to deploy Holocene on this L1, this field should be set to
+# the existing deployment address.
+#
+# If this field is not set, the `superchain-registry` will be consulted for the implementation address.
+# If this field is set to the zero address, a new `SystemConfig` implementation will be deployed.
+SYSTEM_CONFIG_IMPL_ADDR=
+
+# Address of the deployed `MIPS` implementation for Holocene.
+#
+# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`.
+# If you are not the first to deploy Holocene on this L1, this field should be set to
+# the existing deployment address.
+#
+# If this field is not set, the `superchain-registry` will be consulted for the implementation address.
+# If this field is set to the zero address, a new `MIPS` implementation will be deployed.
+MIPS_IMPL_ADDR=
+
+# Address of deployed `DelayedWETH` implementation contract.
+#
+# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`.
+# If you are not the first to deploy permissionless fault proofs on L1, this field should be
+# set to the existing deployment address.
+#
+# If this field is not set, the `superchain-registry` will be consulted for the implementation address.
+# If this field is set to the zero address, a new `DelayedWETH` implementation will be deployed.
+DELAYED_WETH_IMPL_ADDR=
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore
new file mode 100644
index 000000000000..442ed87d9304
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore
@@ -0,0 +1,2 @@
+# Environment
+.env
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol
new file mode 100644
index 000000000000..8203e42b060c
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.15;
+
+// Forge
+import { console2 as console } from "forge-std/console2.sol";
+
+// Scripts
+import { Deployer } from "scripts/deploy/Deployer.sol";
+import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
+
+// Utils
+import { Claim, GameTypes, Duration } from "src/dispute/lib/Types.sol";
+
+// Interfaces
+import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol";
+import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
+import { IProxy } from "src/universal/interfaces/IProxy.sol";
+import {
+    IFaultDisputeGame,
+    IBigStepper,
+    IAnchorStateRegistry,
+    IDelayedWETH
+} from "src/dispute/interfaces/IFaultDisputeGame.sol";
+import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDisputeGame.sol";
+import { IMIPS, IPreimageOracle } from "src/cannon/interfaces/IMIPS.sol";
+
+/// @title DeployUpgrade
+/// @notice A deployment script for smart contract upgrades surrounding the Holocene hardfork.
+contract DeployUpgrade is Deployer {
+    /// @dev The entrypoint to the deployment script.
+    function deploy(
+        address _proxyAdmin,
+        address _superchainConfig,
+        address _systemConfigImpl,
+        address _mipsImpl,
+        address _delayedWETH,
+        address _preimageOracle,
+        address _anchorStateRegistry,
+        bool _useFaultProofs,
+        bool _usePermissionlessFaultProofs
+    )
+        public
+    {
+        // Shim the existing contracts that this upgrade is dependent on.
+        shim({
+            _proxyAdmin: _proxyAdmin,
+            _superchainConfig: _superchainConfig,
+            _systemConfigImpl: _systemConfigImpl,
+            _mipsImpl: _mipsImpl,
+            _delayedWETH: _delayedWETH,
+            _preimageOracle: _preimageOracle,
+            _anchorStateRegistry: _anchorStateRegistry
+        });
+
+        // Deploy conditional implementations.
+        if (_systemConfigImpl == address(0)) deploySystemConfigImplementation();
+
+        if (_useFaultProofs) {
+            if (_mipsImpl == address(0)) deployMIPSImplementation();
+            if (_delayedWETH == address(0)) deployDelayedWETH();
+
+            // Deploy:
+            // 1. New `DelayedWETH` proxy contracts for the `FaultDisputeGame` and `PermissionedDisputeGame`.
+            // 2. New `FaultDisputeGame` and `PermissionedDisputeGame` implementation contracts.
+            deployDelayedWETHProxy("PDG");
+            deployPermissionedDisputeGameImplementation();
+            if (_usePermissionlessFaultProofs) {
+                deployDelayedWETHProxy("FDG");
+                deployFaultDisputeGameImplementation();
+            }
+
+            // Run deployment checks.
+            checkMIPS();
+            checkPermissionedDisputeGame();
+            checkDelayedWETH("PDG");
+            if (_usePermissionlessFaultProofs) {
+                checkFaultDisputeGame();
+                checkDelayedWETH("FDG");
+            }
+        }
+
+        // Print the deployment summary.
+        printSummary();
+    }
+
+    /// @dev Shims the existing contracts that this upgrade is dependent on.
+    function shim(
+        address _proxyAdmin,
+        address _superchainConfig,
+        address _systemConfigImpl,
+        address _mipsImpl,
+        address _delayedWETH,
+        address _preimageOracle,
+        address _anchorStateRegistry
+    )
+        public
+    {
+        prankDeployment("ProxyAdmin", _proxyAdmin);
+        prankDeployment("SuperchainConfig", _superchainConfig);
+        if (_systemConfigImpl != address(0)) prankDeployment("SystemConfig", _systemConfigImpl);
+        if (_mipsImpl != address(0)) prankDeployment("MIPS", _mipsImpl);
+        if (_delayedWETH != address(0)) prankDeployment("DelayedWETH", _delayedWETH);
+        prankDeployment("PreimageOracle", _preimageOracle);
+        prankDeployment("AnchorStateRegistry", _anchorStateRegistry);
+    }
+
+    /// @dev Deploys the Holocene `SystemConfig` implementation contract.
+    function deploySystemConfigImplementation() public {
+        vm.broadcast(msg.sender);
+        address systemConfig = DeployUtils.create1(
+            "SystemConfig", DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ()))
+        );
+        save("SystemConfig", systemConfig);
+    }
+
+    /// @dev Deploys the new `MIPS` implementation contract.
+    function deployMIPSImplementation() public {
+        vm.broadcast(msg.sender);
+        address mips = DeployUtils.create1({
+            _name: "MIPS",
+            _args: DeployUtils.encodeConstructor(
+                abi.encodeCall(IMIPS.__constructor__, (IPreimageOracle(mustGetAddress("PreimageOracle"))))
+            )
+        });
+        save("MIPS", mips);
+    }
+
+    /// @dev Checks if the `MIPS` contract is correctly configured.
+    function checkMIPS() public view {
+        IMIPS mips = IMIPS(mustGetAddress("MIPS"));
+        require(
+            address(mips.oracle()) == mustGetAddress("PreimageOracle"), "DeployHoloceneUpgrade: invalid MIPS oracle"
+        );
+    }
+
+    /// @dev Deploys the Holocene `FaultDisputeGame` implementation contract.
+    function deployFaultDisputeGameImplementation() public {
+        bytes memory constructorInput = abi.encodeCall(
+            IFaultDisputeGame.__constructor__,
+            (
+                GameTypes.CANNON,
+                Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())),
+                cfg.faultGameMaxDepth(),
+                cfg.faultGameSplitDepth(),
+                Duration.wrap(uint64(cfg.faultGameClockExtension())),
+                Duration.wrap(uint64(cfg.faultGameMaxClockDuration())),
+                IBigStepper(mustGetAddress("MIPS")),
+                IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyFDG"))),
+                IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")),
+                cfg.l2ChainID()
+            )
+        );
+
+        vm.broadcast(msg.sender);
+        address fdg = DeployUtils.create1("FaultDisputeGame", DeployUtils.encodeConstructor(constructorInput));
+        save("FaultDisputeGame", fdg);
+    }
+
+    /// @dev Checks if the `FaultDisputeGame` contract is correctly configured.
+    function checkFaultDisputeGame() public view {
+        IFaultDisputeGame fdg = IFaultDisputeGame(mustGetAddress("FaultDisputeGame"));
+        require(
+            fdg.gameType().raw() == GameTypes.CANNON.raw(), "DeployHoloceneUpgrade: invalid FaultDisputeGame gameType"
+        );
+        require(
+            fdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()),
+            "DeployHoloceneUpgrade: invalid FaultDisputeGame absolutePrestate"
+        );
+        require(
+            fdg.maxGameDepth() == cfg.faultGameMaxDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame maxDepth"
+        );
+        require(
+            fdg.splitDepth() == cfg.faultGameSplitDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame splitDepth"
+        );
+        require(
+            fdg.clockExtension().raw() == cfg.faultGameClockExtension(),
+            "DeployHoloceneUpgrade: invalid FaultDisputeGame clockExtension"
+        );
+        require(
+            fdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(),
+            "DeployHoloceneUpgrade: invalid FaultDisputeGame maxClockDuration"
+        );
+        require(address(fdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid FaultDisputeGame MIPS");
+        require(
+            address(fdg.weth()) == mustGetAddress("DelayedWETHProxyFDG"),
+            "DeployHoloceneUpgrade: invalid FaultDisputeGame DelayedWETH"
+        );
+        require(
+            address(fdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"),
+            "DeployHoloceneUpgrade: invalid FaultDisputeGame AnchorStateRegistry"
+        );
+        require(fdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid FaultDisputeGame l2ChainID");
+    }
+
+    /// @dev Deploys the Holocene `PermissionedDisputeGame` implementation contract.
+    function deployPermissionedDisputeGameImplementation() public {
+        bytes memory constructorInput = abi.encodeCall(
+            IPermissionedDisputeGame.__constructor__,
+            (
+                GameTypes.PERMISSIONED_CANNON,
+                Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())),
+                cfg.faultGameMaxDepth(),
+                cfg.faultGameSplitDepth(),
+                Duration.wrap(uint64(cfg.faultGameClockExtension())),
+                Duration.wrap(uint64(cfg.faultGameMaxClockDuration())),
+                IBigStepper(mustGetAddress("MIPS")),
+                IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyPDG"))),
+                IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")),
+                cfg.l2ChainID(),
+                cfg.l2OutputOracleProposer(),
+                cfg.l2OutputOracleChallenger()
+            )
+        );
+
+        vm.broadcast(msg.sender);
+        address fdg = DeployUtils.create1("PermissionedDisputeGame", DeployUtils.encodeConstructor(constructorInput));
+        save("PermissionedDisputeGame", fdg);
+    }
+
+    /// @dev Checks if the `PermissionedDisputeGame` contract is correctly configured.
+    function checkPermissionedDisputeGame() public view {
+        IPermissionedDisputeGame pdg = IPermissionedDisputeGame(mustGetAddress("PermissionedDisputeGame"));
+        require(
+            pdg.gameType().raw() == GameTypes.PERMISSIONED_CANNON.raw(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame gameType"
+        );
+        require(
+            pdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame absolutePrestate"
+        );
+        require(
+            pdg.maxGameDepth() == cfg.faultGameMaxDepth(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxDepth"
+        );
+        require(
+            pdg.splitDepth() == cfg.faultGameSplitDepth(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame splitDepth"
+        );
+        require(
+            pdg.clockExtension().raw() == cfg.faultGameClockExtension(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame clockExtension"
+        );
+        require(
+            pdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxClockDuration"
+        );
+        require(
+            address(pdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame MIPS"
+        );
+        require(
+            address(pdg.weth()) == mustGetAddress("DelayedWETHProxyPDG"),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame DelayedWETH"
+        );
+        require(
+            address(pdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame AnchorStateRegistry"
+        );
+        require(pdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame l2ChainID");
+        require(
+            pdg.proposer() == cfg.l2OutputOracleProposer(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame proposer"
+        );
+        require(
+            pdg.challenger() == cfg.l2OutputOracleChallenger(),
+            "DeployHoloceneUpgrade: invalid PermissionedDisputeGame challenger"
+        );
+    }
+
+    /// @dev Deploys a new implementation of the `DelayedWETH` contract.
+    function deployDelayedWETH() public {
+        uint256 delay = cfg.faultGameWithdrawalDelay();
+
+        vm.broadcast(msg.sender);
+        address impl = DeployUtils.create1({
+            _name: "DelayedWETH",
+            _args: DeployUtils.encodeConstructor(abi.encodeCall(IDelayedWETH.__constructor__, (delay)))
+        });
+
+        // Save the new implementation address.
+        save("DelayedWETH", impl);
+    }
+
+    /// @dev Deploys a new proxy contract with a new `DelayedWETH` implementation.
+    function deployDelayedWETHProxy(string memory _variant) public {
+        address delayedWethOwner = cfg.finalSystemOwner();
+        address proxyAdmin = mustGetAddress("ProxyAdmin");
+        address impl = mustGetAddress("DelayedWETH");
+        ISuperchainConfig superchainConfig = ISuperchainConfig(mustGetAddress("SuperchainConfig"));
+        string memory finalName = string.concat("DelayedWETHProxy", _variant);
+
+        // Deploy the implementation and proxy contracts.
+        vm.broadcast(msg.sender);
+        IProxy proxy = IProxy(
+            DeployUtils.create1({
+                _name: "Proxy",
+                _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender)))
+            })
+        );
+
+        // Upgrade the proxy to the implementation and initialize it.
+        vm.broadcast(msg.sender);
+        proxy.upgradeToAndCall(impl, abi.encodeCall(IDelayedWETH.initialize, (delayedWethOwner, superchainConfig)));
+
+        // Transfer the admin role of the proxy to the ProxyAdmin, now that we've upgraded
+        // and initialized the proxy.
+        vm.broadcast(msg.sender);
+        proxy.changeAdmin(proxyAdmin);
+
+        // Save the proxy address.
+        save(finalName, address(proxy));
+    }
+
+    /// @dev Checks if the `DelayedWETH` contract is correctly configured.
+    function checkDelayedWETH(string memory _variant) internal {
+        string memory finalName = string.concat("DelayedWETHProxy", _variant);
+        IDelayedWETH delayedWeth = IDelayedWETH(mustGetAddress(finalName));
+        require(
+            delayedWeth.delay() == cfg.faultGameWithdrawalDelay(), "DeployHoloceneUpgrade: invalid DelayedWETH delay"
+        );
+        require(
+            delayedWeth.config() == ISuperchainConfig(mustGetAddress("SuperchainConfig")),
+            "DeployHoloceneUpgrade: invalid DelayedWETH config"
+        );
+
+        vm.prank(mustGetAddress("ProxyAdmin"));
+        address admin = IProxy(payable(address(delayedWeth))).admin();
+        require(admin == mustGetAddress("ProxyAdmin"), "DeployHoloceneUpgrade: invalid DelayedWETH admin");
+    }
+
+    /// @dev Prints a summary of the deployment.
+    function printSummary() internal view {
+        console.log("1. SystemConfig: %s", mustGetAddress("SystemConfig"));
+        console.log("2. MIPS: %s", getAddress("MIPS"));
+        console.log("3. FaultDisputeGame: %s", getAddress("FaultDisputeGame"));
+        console.log("4. PermissionedDisputeGame: %s", getAddress("PermissionedDisputeGame"));
+    }
+}
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md
new file mode 100644
index 000000000000..f16127ffebbd
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md
@@ -0,0 +1,41 @@
+# Holocene Upgrade
+
+This directory contains a repeatable task for:
+* upgrading an `op-contracts/v1.6.0` deployment to `op-contracts/v1.8.0`.
+* upgrading an `op-contracts/v1.3.0` deployment to `op-contracts/v1.8.0`, while retaining the `L2OutputOracle`.
+
+## Dependencies
+
+- [`docker`](https://docs.docker.com/engine/install/)
+- [`just`](https://github.com/casey/just)
+- [`foundry`](https://getfoundry.sh/)
+
+## Usage
+
+This script has several different modes of operation. Namely:
+1. Deploy and upgrade `op-contracts/1.6.0` -> `op-contracts/v1.8.0`
+  - Always upgrade the `SystemConfig`
+  - FP options:
+    - With permissionless fault proofs enabled (incl. `FaultDisputeGame`)
+    - With permissioned fault proofs enabled (excl. `FaultDisputeGame`)
+1. Deploy and upgrade `op-contracts/v1.3.0` -> `op-contracts/v1.8.0`, with the `L2OutputOracle` still active.
+  - Only upgrade the `SystemConfig`
+
+```sh
+# 1. Clone the monorepo and navigate to this directory.
+git clone git@github.com:ethereum-optimism/monorepo.git && \
+  cd monorepo/packages/contracts-bedrock/scripts/upgrades/holocene
+
+# 2. Set up the `.env` file
+#
+# Read the documentation carefully, and when in doubt, reach out to the OP Labs team.
+cp .env.example .env && vim .env
+
+# 3. Run the upgrade task.
+#
+#    This task will:
+#    - Deploy the new smart contract implementations.
+#    - Optionally, generate a safe upgrade bundle.
+#    - Optionally, generate a `superchain-ops` upgrade task.
+just run
+```
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/justfile b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile
new file mode 100644
index 000000000000..08cdc771ac92
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile
@@ -0,0 +1,26 @@
+# Default recipe to list help menu.
+default:
+  @just --list
+
+# Run the deployment / upgrade generation image. If the image is not present locally,
+# it will be built.
+run *args='':
+  #!/bin/bash
+  if [ ! "$(docker images -q op-holocene-upgrade:local 2> /dev/null)" ]; then
+    just build-image
+  fi
+
+  # Run the deployment.
+  docker run \
+    --rm \
+    -v $OUTPUT_FOLDER_PATH/:/output \
+    --env-file=.env \
+    op-holocene-upgrade:local {{args}}
+
+# Build the image locally.
+build-image:
+  docker build \
+  -t op-holocene-upgrade:local \
+  -f upgrade.dockerfile \
+  --build-arg REV=op-contracts/v1.8.0-rc.1 \
+  .
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh
new file mode 100755
index 000000000000..0f4e80b29c40
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh
@@ -0,0 +1,131 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Associative array to store cached TOML content for different URLs
+# Used by fetch_standard_address and fetch_superchain_config_address
+declare -A CACHED_TOML_CONTENT
+
+# error_handler
+#
+# Basic error handler
+error_handler() {
+  echo "Error occurred in ${BASH_SOURCE[1]} at line: ${BASH_LINENO[0]}"
+  echo "Error message: $BASH_COMMAND"
+  exit 1
+}
+
+# Register the error handler
+trap error_handler ERR
+
+# reqenv
+#
+# Checks if a specified environment variable is set.
+#
+# Arguments:
+#   $1 - The name of the environment variable to check
+#
+# Exits with status 1 if:
+#   - The specified environment variable is not set
+reqenv() {
+    if [ -z "$1" ]; then
+        echo "Error: $1 is not set"
+        exit 1
+    fi
+}
+
+# prompt
+#
+# Prompts the user for a yes/no response.
+#
+# Arguments:
+#   $1 - The prompt message
+#
+# Exits with status 1 if:
+#   - The user does not respond with 'y'
+#   - The process is interrupted
+prompt() {
+    read -p "$1 [Y/n] " -n 1 -r
+    echo
+    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+        [[ "$0" = "${BASH_SOURCE[0]}" ]] && exit 1 || return 1
+        exit 1
+    fi
+}
+
+# fetch_standard_address
+#
+# Fetches the implementation address for a given contract from a TOML file.
+# The TOML file is downloaded from a URL specified in ADDRESSES_TOML_URL
+# environment variable. Results are cached to avoid repeated downloads.
+#
+# Arguments:
+#   $1 - Network name
+#   $2 - The release version
+#   $3 - The name of the contract to look up
+#
+# Returns:
+#   The implementation address of the specified contract
+#
+# Exits with status 1 if:
+#   - Failed to fetch the TOML file
+#   - The release version is not found in the TOML file
+#   - The implementation address for the specified contract is not found
+fetch_standard_address() {
+    local network_name="$1"
+    local release_version="$2"
+    local contract_name="$3"
+
+    # Determine the correct toml url
+    local toml_url="https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions"
+    if [ "$network_name" = "mainnet" ]; then
+        toml_url="$toml_url-mainnet.toml"
+    elif [ "$network_name" = "sepolia" ]; then
+        toml_url="$toml_url-sepolia.toml"
+    else
+        echo "Error: NETWORK must be set to 'mainnet' or 'sepolia'"
+        exit 1
+    fi
+
+    # Fetch the TOML file content from the URL if not already cached for this URL
+    if [ -z "${CACHED_TOML_CONTENT[$toml_url]:-}" ]; then
+        CACHED_TOML_CONTENT[$toml_url]=$(curl -s "$toml_url")
+        # shellcheck disable=SC2181
+        if [ $? -ne 0 ]; then
+            echo "Error: Failed to fetch TOML file from $toml_url"
+            exit 1
+        fi
+    fi
+
+    # Use the cached content for the current URL
+    local toml_content="${CACHED_TOML_CONTENT[$toml_url]}"
+
+    # Find the section for v1.6.0 release
+    # shellcheck disable=SC2155
+    local section_content=$(echo "$toml_content" | awk -v version="$release_version" '
+        $0 ~ "^\\[releases.\"op-contracts/v" version "\"\\]" {
+            flag=1;
+            next
+        }
+        flag && /^\[/ {
+            exit
+        }
+        flag {
+            print
+        }
+    ')
+    if [ -z "$section_content" ]; then
+        echo "Error: v$release_version release section not found in addresses TOML"
+        exit 1
+    fi
+
+    # Extract the implementation address for the specified contract
+    local regex="(address|implementation_address) = \"(0x[a-fA-F0-9]{40})\""
+    # shellcheck disable=SC2155
+    local data=$(echo "$section_content" | grep "${contract_name}")
+    if [[ $data =~ $regex ]]; then
+        echo "${BASH_REMATCH[2]}"
+    else
+        echo "Error: Implementation address for $contract_name not found in v$release_version release"
+        exit 1
+    fi
+}
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh
new file mode 100755
index 000000000000..d1e15aa63e7f
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+# Check required environment variables
+reqenv "ETH_RPC_URL"
+reqenv "PRIVATE_KEY"
+reqenv "ETHERSCAN_API_KEY"
+reqenv "DEPLOY_CONFIG_PATH"
+reqenv "IMPL_SALT"
+
+# Check required address environment variables
+reqenv "PROXY_ADMIN_ADDR"
+reqenv "SUPERCHAIN_CONFIG_PROXY_ADDR"
+reqenv "PREIMAGE_ORACLE_ADDR"
+reqenv "ANCHOR_STATE_REGISTRY_PROXY_ADDR"
+reqenv "DELAYED_WETH_IMPL_ADDR"
+reqenv "SYSTEM_CONFIG_IMPL_ADDR"
+reqenv "MIPS_IMPL_ADDR"
+reqenv "USE_FAULT_PROOFS"
+reqenv "USE_PERMISSIONLESS_FAULT_PROOFS"
+
+# Run the upgrade script
+forge script DeployUpgrade.s.sol \
+  --rpc-url "$ETH_RPC_URL" \
+  --private-key "$PRIVATE_KEY" \
+  --etherscan-api-key "$ETHERSCAN_API_KEY" \
+  --sig "deploy(address,address,address,address,address,address,address,bool,bool)" \
+  "$PROXY_ADMIN_ADDR" \
+  "$SUPERCHAIN_CONFIG_PROXY_ADDR" \
+  "$SYSTEM_CONFIG_IMPL_ADDR" \
+  "$MIPS_IMPL_ADDR" \
+  "$DELAYED_WETH_IMPL_ADDR" \
+  "$PREIMAGE_ORACLE_ADDR" \
+  "$ANCHOR_STATE_REGISTRY_PROXY_ADDR" \
+  "$USE_FAULT_PROOFS" \
+  "$USE_PERMISSIONLESS_FAULT_PROOFS" \
+  --broadcast \
+  --verify \
+  --slow
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh
new file mode 100755
index 000000000000..95fe1a43c231
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+echo "
+⠄⢀⠀⠀⡐⠠⠀⢂⣠⡤⣤⣖⣤⣤⣄⢢⣤⣤⣤⡀⠄⠰⠀⠆⠀⠀⠠⠀⠆⠠⢀⠢⠐⠆⡀
+⠔⢀⠀⠄⡀⠔⢠⣾⣿⣿⣷⣿⣿⣿⣿⣷⣟⣛⣻⣿⣤⡠⢀⠆⢄⠂⠠⢠⠀⠄⠀⢠⠰⢄⠄
+⣈⠀⠐⡀⠀⠈⣾⣿⣿⣿⣿⡿⡿⣿⣿⣿⣿⡿⢿⣿⣿⣿⣦⠀⠂⠀⢈⠀⠁⠂⡀⢀⠠⠈⡀
+⠆⠈⠀⠄⣃⣼⣿⣿⣿⠿⡿⣿⣿⣷⣾⣷⣾⡾⣿⣿⣿⢿⡟⡇⠠⠁⠨⠐⠀⠃⠱⠊⠀⠀⠄
+⠐⣠⣶⣿⣿⣿⣿⣿⣿⣿⣾⣮⣛⢿⣿⡿⠘⣇⢯⣹⣶⣷⣿⣿⡄⠆⢐⠀⢄⠢⡒⠐⠠⢀⠂
+⢴⣿⣿⣿⣿⣿⣿⡿⠿⢿⣿⣿⣟⢿⣶⣾⣿⣧⢻⣿⢿⣿⣿⣿⠂⠈⢀⠈⡀⡁⢀⠉⢀⠀⠀
+⡜⣿⣿⣿⣿⣿⣿⣦⡻⠀⢨⣽⣿⣿⣿⣿⣿⣿⣦⡛⣾⣭⣃⣀⣦⠀⠨⠐⠀⠄⠃⠈⠀⠀⠁
+⣳⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⢛⣭⣿⣿⣿⣿⣿⣿⣿⢢⣿⣿⣿⣿⡇⢀⠲⠂⠄⢀⡐⠠⠐⠂
+⢣⠵⢹⣿⣿⣿⣿⣿⣿⣿⣿⣧⣛⣻⣿⣭⣿⣿⠻⢥⣿⣿⣿⣿⣿⣿⡄⠀⠁⡀⢀⢈⠀⢀⠁
+⡼⣹⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣟⣻⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠁⠉⠐⠀⠂⠈
+⡷⣡⠧⢹⣿⣿⣿⣿⣿⣿⣗⣶⣾⣿⣿⣿⣿⣿⠮⢿⣿⣿⡿⠿⣿⣿⢀⣀⠂⡁⠐⢌⠐⡠⠁
+⢷⠡⠏⢧⢌⢻⣿⣿⣿⣟⣿⣿⣻⣿⡿⠿⣛⣵⢟⣭⡭⠥⠮⠕⣒⣒⣚⣮⣰⢤⣤⣄⣀⡠⠄
+⠁⠠⠀⠄⣏⢦⡙⣿⣿⣽⣟⣿⡟⣬⣾⣿⣿⣿⣾⣆⠭⠽⠶⢶⢶⣖⣶⣹⣖⡿⣿⣿⣿⣿⡆
+⠀⡁⠂⡟⡜⣦⢫⣌⠻⣷⣿⢏⣾⣿⣿⣿⢿⣿⢿⣿⣿⣿⣿⣿⠿⣛⣛⣛⠛⣭⣿⣿⢻⣽⡇
+⣏⠖⣮⢱⣋⢖⣣⢎⡳⢤⡅⣾⣿⢯⣷⡏⡖⣰⣝⣚⣙⣫⢍⡶⠷⣽⢭⣛⡇⠀⢰⣶⣾⣿⠀
+⡮⡝⢦⣓⢎⡳⡜⠎⠡⠁⢸⣿⣿⣿⣿⣁⢠⣿⡿⣿⡟⣎⣯⣽⣋⡷⣾⣹⡃⠀⢸⣿⣿⢿⠀
+⣳⠁⠀⡝⢮⣱⢹⠀⠂⠈⣿⣿⣿⣿⡻⣈⣸⣿⣙⢾⢿⣹⡶⣿⣼⣗⣻⡞⣡⠁⣼⣿⣿⣿⡀
+⡔⢦⢥⡛⣜⠦⣏⡄⡈⣸⢿⣿⡿⣽⢃⠇⣿⣧⡝⣟⡳⢾⣹⣟⡻⣾⣹⢣⠞⣄⣯⣿⣷⣿⡆
+   -*~ [ Grug Deployer mk2 ] ~*-
+       ~*- [ Holocene ] -*~
+"
+
+# Set variables from environment or error.
+export RELEASE="1.8.0"
+export NETWORK="${NETWORK:?NETWORK must be set}"
+export ETHERSCAN_API_KEY=${ETHERSCAN_API_KEY:?ETHERSCAN_API_KEY must be set}
+export ETH_RPC_URL=${ETH_RPC_URL:?ETH_RPC_URL must be set}
+export PRIVATE_KEY=${PRIVATE_KEY:?PRIVATE_KEY must be set}
+export BASE_DEPLOY_CONFIG_PATH=${DEPLOY_CONFIG_PATH:?DEPLOY_CONFIG_PATH must be set}
+export OUTPUT_FOLDER_PATH=${OUTPUT_FOLDER_PATH:?OUTPUT_FOLDER_PATH must be set}
+export SYSTEM_CONFIG_IMPL_ADDR=${SYSTEM_CONFIG_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "system_config")}
+export MIPS_IMPL_ADDR=${MIPS_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "mips")}
+export PREIMAGE_ORACLE_ADDR=${PREIMAGE_ORACLE_ADDR:?PREIMAGE_ORACLE_ADDR must be set}
+export ANCHOR_STATE_REGISTRY_PROXY_ADDR=${ANCHOR_STATE_REGISTRY_PROXY_ADDR:?ANCHOR_STATE_REGISTRY_PROXY_ADDR must be set}
+export DELAYED_WETH_IMPL_ADDR=${DELAYED_WETH_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "delayed_weth")}
+export PROXY_ADMIN_ADDR=${PROXY_ADMIN_ADDR:?PROXY_ADMIN_ADDR must be set}
+export SUPERCHAIN_CONFIG_PROXY_ADDR=${SUPERCHAIN_CONFIG_PROXY_ADDR:?SUPERCHAIN_CONFIG_ADDR must be set}
+export SYSTEM_CONFIG_PROXY_ADDR=${SYSTEM_CONFIG_PROXY_ADDR:?SYSTEM_CONFIG_PROXY_ADDR must be set}
+export DISPUTE_GAME_FACTORY_PROXY_ADDR=${DISPUTE_GAME_FACTORY_PROXY_ADDR:?DISPUTE_GAME_FACTORY_PROXY_ADDR must be set}
+export USE_FAULT_PROOFS=${USE_FAULT_PROOFS:?USE_FAULT_PROOFS must be set}
+export USE_PERMISSIONLESS_FAULT_PROOFS=${USE_PERMISSIONLESS_FAULT_PROOFS:?USE_PERMISSIONLESS_FAULT_PROOFS must be set}
+
+# Sanity check FP configuration.
+if [[ $USE_PERMISSIONLESS_FAULT_PROOFS == true && $USE_FAULT_PROOFS == false ]]; then
+    echo "Error: USE_PERMISSIONLESS_FAULT_PROOFS cannot be true if USE_FAULT_PROOFS is false"
+    exit 1
+fi
+
+# Make the output folder, if it doesn't exist
+mkdir -p "$OUTPUT_FOLDER_PATH"
+
+# Find the contracts-bedrock directory
+CONTRACTS_BEDROCK_DIR=$(pwd)
+while [[ "$CONTRACTS_BEDROCK_DIR" != "/" && "${CONTRACTS_BEDROCK_DIR##*/}" != "contracts-bedrock" ]]; do
+    CONTRACTS_BEDROCK_DIR=$(dirname "$CONTRACTS_BEDROCK_DIR")
+done
+
+# Error out if we couldn't find it for some reason
+if [[ "$CONTRACTS_BEDROCK_DIR" == "/" ]]; then
+    echo "Error: 'contracts-bedrock' directory not found"
+    exit 1
+fi
+
+# Set file paths from command-line arguments
+export DEPLOY_CONFIG_PATH="$CONTRACTS_BEDROCK_DIR/deploy-config/deploy-config.json"
+
+# Copy the files into the paths so that the script can actually access it
+cp "$BASE_DEPLOY_CONFIG_PATH" "$DEPLOY_CONFIG_PATH"
+
+# Run deploy.sh
+DEPLOY_LOG_PATH="$OUTPUT_FOLDER_PATH/deploy.log"
+if ! "$SCRIPT_DIR/deploy.sh" | tee "$DEPLOY_LOG_PATH"; then
+    echo "Error: deploy.sh failed"
+    exit 1
+fi
+
+# Extract the addresses from the deployment logs
+# shellcheck disable=2155
+export SYSTEM_CONFIG_IMPL=$(grep "1. SystemConfig:" "$DEPLOY_LOG_PATH" | awk '{print $3}')
+# shellcheck disable=2155
+export MIPS_IMPL=$(grep "2. MIPS:" "$DEPLOY_LOG_PATH" | awk '{print $3}')
+# shellcheck disable=2155
+export FDG_IMPL=$(grep "3. FaultDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}')
+# shellcheck disable=2155
+export PDG_IMPL=$(grep "4. PermissionedDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}')
+
+# Ensure that the addresses were extracted properly
+reqenv "SYSTEM_CONFIG_IMPL"
+reqenv "MIPS_IMPL"
+reqenv "FDG_IMPL"
+reqenv "PDG_IMPL"
+
+# Generate deployments.json with extracted addresses
+DEPLOYMENTS_JSON_PATH="$OUTPUT_FOLDER_PATH/deployments.json"
+cat << EOF > "$DEPLOYMENTS_JSON_PATH"
+{
+  "SystemConfig": "$SYSTEM_CONFIG_IMPL",
+  "MIPS": "$MIPS_IMPL",
+  "FaultDisputeGame": "$FDG_IMPL",
+  "PermissionedDisputeGame": "$PDG_IMPL"
+}
+EOF
+
+echo "✨ Deployed contracts and saved addresses to \"$DEPLOYMENTS_JSON_PATH\""
+
+# Print a message when the script exits
+trap 'echo "✨ Done. Artifacts are available in \"$OUTPUT_FOLDER_PATH\""' EXIT
+
+prompt "Generate safe upgrade bundle for SystemConfig?"
+
+# Generate the system config upgrade bundle
+if ! "$SCRIPT_DIR/sys-cfg-bundle.sh"; then
+    echo "Error: sys-cfg-bundle.sh failed"
+    exit 1
+fi
+
+prompt "Generate superchain-ops upgrade task for SystemConfig upgrade bundle?"
+
+# Generate the superchain-ops upgrade task
+if ! "$SCRIPT_DIR/sc-ops-sys-cfg.sh"; then
+    echo "Error: sc-ops-sys-cfg.sh failed"
+    exit 1
+fi
+
+if [[ $USE_FAULT_PROOFS == true ]]; then
+  prompt "Generate safe upgrade bundle for proofs contracts?"
+
+  # Generate the proofs contracts' upgrade bundle
+  if ! "$SCRIPT_DIR/proofs-bundle.sh"; then
+      echo "Error: proofs-bundle.sh failed"
+      exit 1
+  fi
+
+  prompt "Generate superchain-ops upgrade task for proofs contracts upgrade bundle?"
+
+  # Generate the superchain-ops upgrade task
+  if ! "$SCRIPT_DIR/sc-ops-proofs.sh"; then
+      echo "Error: sc-ops-proofs.sh failed"
+      exit 1
+  fi
+fi
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh
new file mode 100755
index 000000000000..85295d3040f1
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+# Check the env
+reqenv "ETH_RPC_URL"
+reqenv "OUTPUT_FOLDER_PATH"
+reqenv "MIPS_IMPL"
+reqenv "FDG_IMPL"
+reqenv "PDG_IMPL"
+reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR"
+reqenv "USE_PERMISSIONLESS_FAULT_PROOFS"
+
+# Local environment
+BUNDLE_PATH="$OUTPUT_FOLDER_PATH/proofs_bundle.json"
+L1_CHAIN_ID=$(cast chain-id)
+
+# Copy the bundle template
+cp ./templates/proof_upgrade_bundle_template.json "$BUNDLE_PATH"
+
+# Tx 1: Upgrade PermissionedDisputeGame implementation
+TX_1_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 1 "$PDG_IMPL")
+
+# Tx 2: Upgrade FaultDisputeGame implementation
+TX_2_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 0 "$FDG_IMPL")
+
+# Replace variables
+sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH"
+sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$BUNDLE_PATH"
+sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH"
+sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH"
+
+# Conditionally, if the FDG is being deployed, append the bundle extension
+if [ "$USE_PERMISSIONLESS_FAULT_PROOFS" == true ]; then
+  echo "✨ USE_PERMISSIONLESS_FAULT_PROOFS=true | Adding FDG deployment to upgrade bundle."
+  jq --argjson fdg_extension "$(cat ./templates/fdg_bundle_extension.json)" \
+    '.transactions += [$fdg_extension]' \
+    "$BUNDLE_PATH" > "$BUNDLE_PATH.tmp"
+  mv "$BUNDLE_PATH.tmp" "$BUNDLE_PATH"
+
+  # Replace variables
+  sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$BUNDLE_PATH"
+  sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH"
+fi
+
+sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$BUNDLE_PATH"
+
+echo "✨ Generated proof contracts upgrade bundle at \"$BUNDLE_PATH\""
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh
new file mode 100755
index 000000000000..d4ff18db5738
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+# Check required environment variables
+reqenv "OUTPUT_FOLDER_PATH"
+reqenv "MIPS_IMPL"
+reqenv "FDG_IMPL"
+reqenv "PDG_IMPL"
+reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR"
+
+# Create directory for the task
+TASK_DIR="$OUTPUT_FOLDER_PATH/proofs-sc-ops-task"
+mkdir -p "$TASK_DIR"
+
+# Copy the bundle and task template
+cp "$OUTPUT_FOLDER_PATH/proofs_bundle.json" "$TASK_DIR/input.json"
+cp -R "$SCRIPT_DIR/../templates/proofs-sc-ops-task/." "$TASK_DIR/"
+
+# Generate the task overview
+msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md"
+
+# Generate the README
+sed -i '' "s/\$MIPS_IMPL/$MIPS_IMPL/g" "$TASK_DIR/README.md"
+sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$TASK_DIR/README.md"
+sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$TASK_DIR/README.md"
+
+# Generate the validation doc
+OLD_FDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 0)
+OLD_PDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 1)
+
+PADDED_OLD_FDG=$(cast 2u "$OLD_FDG")
+PADDED_OLD_PDG=$(cast 2u "$OLD_PDG")
+PADDED_FDG_IMPL=$(cast 2u "$FDG_IMPL")
+PADDED_PDG_IMPL=$(cast 2u "$PDG_IMPL")
+
+sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$OLD_FDG/$PADDED_OLD_FDG/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$FDG_IMPL/$PADDED_FDG_IMPL/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$PDG_IMPL/$PADDED_PDG_IMPL/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$OLD_PDG/$PADDED_OLD_PDG/g" "$TASK_DIR/VALIDATION.md"
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh
new file mode 100755
index 000000000000..b0e4a57b32b7
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+# Check required environment variables
+reqenv "OUTPUT_FOLDER_PATH"
+reqenv "SYSTEM_CONFIG_IMPL"
+reqenv "SYSTEM_CONFIG_PROXY_ADDR"
+
+# Create directory for the task
+TASK_DIR="$OUTPUT_FOLDER_PATH/sys-cfg-sc-ops-task"
+mkdir -p "$TASK_DIR"
+
+# Copy the bundle and task template
+cp "$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json" "$TASK_DIR/input.json"
+cp -R "$SCRIPT_DIR/../templates/sys-cfg-sc-ops-task/." "$TASK_DIR/"
+
+# Generate the task overview
+msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md"
+
+# Generate the README
+sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$TASK_DIR/README.md"
+
+# Generate the validation doc
+OLD_SYS_CFG=$(cast impl "$SYSTEM_CONFIG_PROXY_ADDR")
+
+PADDED_OLD_SYS_CFG=$(cast 2u "$OLD_SYS_CFG")
+PADDED_SYS_CFG=$(cast 2u "$SYSTEM_CONFIG_IMPL")
+
+sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$OLD_SYS_CFG/$PADDED_OLD_SYS_CFG/g" "$TASK_DIR/VALIDATION.md"
+sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$PADDED_SYS_CFG/g" "$TASK_DIR/VALIDATION.md"
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh
new file mode 100755
index 000000000000..64c34088ebf5
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Grab the script directory
+SCRIPT_DIR=$(dirname "$0")
+
+# Load common.sh
+# shellcheck disable=SC1091
+source "$SCRIPT_DIR/common.sh"
+
+# Check the env
+reqenv "ETH_RPC_URL"
+reqenv "OUTPUT_FOLDER_PATH"
+reqenv "PROXY_ADMIN_ADDR"
+reqenv "SYSTEM_CONFIG_PROXY_ADDR"
+reqenv "SYSTEM_CONFIG_IMPL"
+
+# Local environment
+BUNDLE_PATH="$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json"
+L1_CHAIN_ID=$(cast chain-id)
+
+# Copy the bundle template
+cp ./templates/sys_cfg_upgrade_bundle_template.json "$BUNDLE_PATH"
+
+# Tx 1: Upgrade SystemConfigProxy implementation
+TX_1_PAYLOAD=$(cast calldata "upgrade(address,address)" "$SYSTEM_CONFIG_PROXY_ADDR" "$SYSTEM_CONFIG_IMPL")
+
+# Replace variables
+sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH"
+sed -i '' "s/\$PROXY_ADMIN_ADDR/$PROXY_ADMIN_ADDR/g" "$BUNDLE_PATH"
+sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$BUNDLE_PATH"
+sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$BUNDLE_PATH"
+sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH"
+
+echo "✨ Generated SystemConfig upgrade bundle at \"$BUNDLE_PATH\""
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json
new file mode 100644
index 000000000000..fa6680496bdb
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json
@@ -0,0 +1,29 @@
+{
+  "metadata": {
+    "name": "Upgrade `CANNON` game type in `DisputeGameFactory`",
+    "description": "Upgrades the `CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash."
+  },
+  "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR",
+  "value": "0x0",
+  "data": "$TX_2_PAYLOAD",
+  "contractMethod": {
+    "type": "function",
+    "name": "setImplementation",
+    "inputs": [
+      {
+        "name": "_gameType",
+        "type": "uint32"
+      },
+      {
+        "name": "_impl",
+        "type": "address"
+      }
+    ],
+    "outputs": [],
+    "stateMutability": "nonpayable"
+  },
+  "contractInputsValues": {
+    "_gameType": "0",
+    "_impl": "$FDG_IMPL"
+  }
+}
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json
new file mode 100644
index 000000000000..e74360f02a97
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json
@@ -0,0 +1,38 @@
+{
+  "chainId": $L1_CHAIN_ID,
+  "metadata": {
+    "name": "Holocene Hardfork - Proof Contract Upgrades",
+    "description": "Upgrades the `MIPS.sol`, `FaultDisputeGame.sol`, and `PermissionedDisputeGame.sol` contracts for Holocene."
+  },
+  "transactions": [
+    {
+      "metadata": {
+        "name": "Upgrade `PERMISSIONED_CANNON` game type in `DisputeGameFactory`",
+        "description": "Upgrades the `PERMISSIONED_CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash."
+      },
+      "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR",
+      "value": "0x0",
+      "data": "$TX_1_PAYLOAD",
+      "contractMethod": {
+        "type": "function",
+        "name": "setImplementation",
+        "inputs": [
+          {
+            "name": "_gameType",
+            "type": "uint32"
+          },
+          {
+            "name": "_impl",
+            "type": "address"
+          }
+        ],
+        "outputs": [],
+        "stateMutability": "nonpayable"
+      },
+      "contractInputsValues": {
+        "_gameType": "1",
+        "_impl": "$PDG_IMPL"
+      }
+    }
+  ]
+}
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example
new file mode 100644
index 000000000000..ba2beff7f429
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example
@@ -0,0 +1,6 @@
+ETH_RPC_URL=
+SUPERCHAIN_CONFIG_ADDR=
+COUNCIL_SAFE=
+FOUNDATION_SAFE=
+OWNER_SAFE=
+SAFE_NONCE=
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md
new file mode 100644
index 000000000000..e2077747dba1
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md
@@ -0,0 +1,46 @@
+# Holocene Hardfork Upgrade
+
+Status: DRAFT, NOT READY TO SIGN
+
+## Objective
+
+Upgrades the Fault Proof contracts for the Holocene hardfork.
+
+The proposal was:
+
+- [ ] Posted on the governance forum.
+- [ ] Approved by Token House voting.
+- [ ] Not vetoed by the Citizens' house.
+- [ ] Executed on OP Mainnet.
+
+The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations.
+
+Governance post of the upgrade can be found at .
+
+This upgrades the Fault Proof contracts in the
+[op-contracts/v1.8.0](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.1) release.
+
+## Pre-deployments
+
+- `MIPS` - `$MIPS_IMPL`
+- `FaultDisputeGame` - `$FDG_IMPL`
+- `PermissionedDisputeGame` - `$PDG_IMPL`
+
+## Simulation
+
+Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md).
+When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`.
+This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run.
+
+## State Validation
+
+Please see the instructions for [validation](./VALIDATION.md).
+
+## Execution
+
+This upgrade
+* Changes dispute game implementation of the `CANNON` and `PERMISSIONED_CANNON` game types to contain a `op-program` release for the Holocene hardfork, which contains
+  the Holocene fork implementation as well as a `ChainConfig` and `RollupConfig` for the L2 chain being upgraded.
+* Upgrades `MIPS.sol` to support the `F_GETFD` syscall, required by the golang 1.22+ runtime.
+
+See the [overview](./OVERVIEW.md) and `input.json` bundle for more details.
diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md
new file mode 100644
index 000000000000..50ba3ca106b4
--- /dev/null
+++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md
@@ -0,0 +1,24 @@
+# Validation
+
+This document can be used to validate the state diff resulting from the execution of the upgrade
+transaction.
+
+For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract:
+
+- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur.
+- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic.
+- All key values match the semantic meaning provided, which can be validated using the storage layout links provided.
+
+## State Changes
+
+### `$DISPUTE_GAME_FACTORY_PROXY_ADDR` (`DisputeGameFactoryProxy`)
+
+- **Key**: `0xffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b` 
+ **Before**: `$OLD_FDG`
+ **After**: `$FDG_IMPL`
+ **Meaning**: Updates the CANNON game type implementation. Verify that the new implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 0`. Where `0` is the [`CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L28). + +- **Key**: `0x4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e`
+ **Before**: `$OLD_PDG`
+ **After**: `$PDG_IMPL`
+ **Meaning**: Updates the PERMISSIONED_CANNON game type implementation. Verify that the new implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 1`. Where `1` is the [`PERMISSIONED_CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L31). diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example new file mode 100644 index 000000000000..ba2beff7f429 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example @@ -0,0 +1,6 @@ +ETH_RPC_URL= +SUPERCHAIN_CONFIG_ADDR= +COUNCIL_SAFE= +FOUNDATION_SAFE= +OWNER_SAFE= +SAFE_NONCE= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md new file mode 100644 index 000000000000..740219586b80 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md @@ -0,0 +1,42 @@ +# Holocene Hardfork Upgrade - `SystemConfig` + +Status: DRAFT, NOT READY TO SIGN + +## Objective + +Upgrades the `SystemConfig` for the Holocene hardfork. + +The proposal was: + +- [ ] Posted on the governance forum. +- [ ] Approved by Token House voting. +- [ ] Not vetoed by the Citizens' house. +- [ ] Executed on OP Mainnet. + +The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations. + +Governance post of the upgrade can be found at . + +This upgrades the `SystemConfig` in the +[op-contracts/v1.8.0](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.1) release. + +## Pre-deployments + +- `SystemConfig` - `$SYSTEM_CONFIG_IMPL` + +## Simulation + +Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md). +When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`. +This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run. + +## State Validation + +Please see the instructions for [validation](./VALIDATION.md). + +## Execution + +This upgrade +* Changes the implementation of the `SystemConfig` to hold EIP-1559 parameters for the + +See the [overview](./OVERVIEW.md) and `input.json` bundle for more details. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md new file mode 100644 index 000000000000..3a5e7b67595a --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md @@ -0,0 +1,19 @@ +# Validation + +This document can be used to validate the state diff resulting from the execution of the upgrade +transaction. + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +## State Changes + +### `$SYSTEM_CONFIG_PROXY_ADDR` (`SystemConfigProxy`) + +- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + **Before**: `$OLD_SYS_CFG` + **After**: `$SYSTEM_CONFIG_IMPL` + **Meaning**: Updates the `SystemConfig` proxy implementation. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json new file mode 100644 index 000000000000..62746a51982f --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json @@ -0,0 +1,38 @@ +{ + "chainId": $L1_CHAIN_ID, + "metadata": { + "name": "Holocene Hardfork - SystemConfig Upgrade", + "description": "Upgrades the `SystemConfig.sol` contract for Holocene." + }, + "transactions": [ + { + "metadata": { + "name": "Upgrade `SystemConfig` proxy", + "description": "Upgrades the `SystemConfig` proxy to the new implementation, featuring configurable EIP-1559 parameters." + }, + "to": "$PROXY_ADMIN_ADDR", + "value": "0x0", + "data": "$TX_1_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "_proxy", + "type": "address" + }, + { + "name": "_implementation", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_proxy": "$SYSTEM_CONFIG_PROXY_ADDR", + "_implementation": "$SYSTEM_CONFIG_IMPL" + } + } + ] +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile new file mode 100644 index 000000000000..f91ff8dadd83 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile @@ -0,0 +1,62 @@ +# Use a base image with necessary tools +FROM ubuntu:20.04 + +ARG REV + +# Install required packages +RUN apt-get update && apt-get install -y \ + git \ + bash \ + curl \ + build-essential \ + jq \ + && rm -rf /var/lib/apt/lists/* + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install just +RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + +# Install msup +RUN git clone https://github.com/clabby/msup.git && \ + cd msup && \ + cargo install --path . + +# Install foundryup +RUN curl -L https://foundry.paradigm.xyz | bash +ENV PATH="/root/.foundry/bin:${PATH}" + +# Set the working directory +WORKDIR /app + +# Clone the repository +RUN git clone https://github.com/ethereum-optimism/optimism.git . + +# Check out the target branch +RUN git checkout $REV + +# Set the working directory to the root of the monorepo +WORKDIR /app + +# Install correct foundry version +RUN just update-foundry + +# Set the working directory to the root of the contracts package +WORKDIR /app/packages/contracts-bedrock + +# Install dependencies +RUN just install + +# Build the contracts package +RUN forge build + +# Deliberately run the upgrade script with invalid args to trigger a build +RUN forge script ./scripts/upgrades/holocene/DeployUpgrade.s.sol || true + +# Set the working directory to where upgrade.sh is located +WORKDIR /app/packages/contracts-bedrock/scripts/upgrades/holocene + +# Set the entrypoint to the main.sh script +ENTRYPOINT ["./scripts/main.sh"] From 4a677e847c7528be92b2605e9b43ef2d024e2fcc Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Fri, 15 Nov 2024 00:30:02 -0800 Subject: [PATCH 181/451] bound duration spent in state publishing loop (#12909) --- op-batcher/batcher/driver.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 6d8b6eb9da1f..6e237b176d8d 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "math/big" _ "net/http/pprof" "sync" @@ -430,7 +431,7 @@ func (l *BatchSubmitter) loop() { defer ticker.Stop() publishAndWait := func() { - l.publishStateToL1(queue, receiptsCh, daGroup) + l.publishStateToL1(queue, receiptsCh, daGroup, time.Duration(math.MaxInt64)) if !l.Txmgr.IsClosed() { if l.Config.UseAltDA { l.Log.Info("Waiting for altDA writes to complete...") @@ -470,7 +471,7 @@ func (l *BatchSubmitter) loop() { l.clearState(l.shutdownCtx) continue } - l.publishStateToL1(queue, receiptsCh, daGroup) + l.publishStateToL1(queue, receiptsCh, daGroup, l.Config.PollInterval) case <-l.shutdownCtx.Done(): if l.Txmgr.IsClosed() { l.Log.Info("Txmgr is closed, remaining channel data won't be sent") @@ -614,9 +615,11 @@ func (l *BatchSubmitter) waitNodeSync() error { return dial.WaitRollupSync(l.shutdownCtx, l.Log, rollupClient, l1TargetBlock, time.Second*12) } -// publishStateToL1 queues up all pending TxData to be published to the L1, returning when there is -// no more data to queue for publishing or if there was an error queing the data. -func (l *BatchSubmitter) publishStateToL1(queue *txmgr.Queue[txRef], receiptsCh chan txmgr.TxReceipt[txRef], daGroup *errgroup.Group) { +// publishStateToL1 queues up all pending TxData to be published to the L1, returning when there is no more data to +// queue for publishing or if there was an error queing the data. maxDuration tells this function to return from state +// publishing after this amount of time has been exceeded even if there is more data remaining. +func (l *BatchSubmitter) publishStateToL1(queue *txmgr.Queue[txRef], receiptsCh chan txmgr.TxReceipt[txRef], daGroup *errgroup.Group, maxDuration time.Duration) { + start := time.Now() for { // if the txmgr is closed, we stop the transaction sending if l.Txmgr.IsClosed() { @@ -634,6 +637,10 @@ func (l *BatchSubmitter) publishStateToL1(queue *txmgr.Queue[txRef], receiptsCh } return } + if time.Since(start) > maxDuration { + l.Log.Warn("Aborting state publishing, max duration exceeded") + return + } } } From 6c4751c7fa2e3d010152ee4f78f7f1463d75c06c Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 15 Nov 2024 12:48:55 +0100 Subject: [PATCH 182/451] improve tests (#12936) --- .../test/L2/L2StandardBridge.t.sol | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol index 86100b6e4d40..fb68ce093187 100644 --- a/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2StandardBridge.t.sol @@ -34,6 +34,7 @@ contract L2StandardBridge_Test is CommonTest { assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check messenger"); assertEq(address(impl.OTHER_BRIDGE()), address(0), "constructor zero check OTHER_BRIDGE"); assertEq(address(impl.otherBridge()), address(0), "constructor zero check otherBridge"); + assertEq(address(impl.l1TokenBridge()), address(0), "constructor zero check l1TokenBridge"); } /// @dev Tests that the bridge is initialized correctly. @@ -378,6 +379,12 @@ contract L2StandardBridge_BridgeERC20_Test is PreBridgeERC20 { assertEq(L2Token.balanceOf(alice), 0); } + function test_bridgeERC20_isNotCorrectTokenPair_reverts() external { + vm.expectRevert("StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"); + vm.prank(alice, alice); + l2StandardBridge.bridgeERC20(address(L2Token), address(BadL1Token), 100, 1000, hex""); + } + function test_withdrawLegacyERC20_succeeds() external { _preBridgeERC20({ _isLegacy: true, _l2Token: address(LegacyL2Token) }); l2StandardBridge.withdraw(address(LegacyL2Token), 100, 1000, hex""); @@ -659,3 +666,43 @@ contract L2StandardBridge_FinalizeBridgeETH_Test is CommonTest { l2StandardBridge.finalizeBridgeETH(alice, alice, 1, hex""); } } + +contract L2StandardBridge_FinalizeBridgeERC20_Test is CommonTest { + /// @dev Tests that `finalizeBridgeERC20` succeeds. + function test_finalizeBridgeERC20_succeeds() external { + address messenger = address(l2StandardBridge.messenger()); + address localToken = address(L2Token); + address remoteToken = address(L1Token); + vm.mockCall( + messenger, + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), + abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) + ); + deal(localToken, messenger, 100, true); + vm.prank(messenger); + + vm.expectEmit(true, true, true, true); + emit DepositFinalized(remoteToken, localToken, alice, alice, 100, hex""); + + vm.expectEmit(true, true, true, true); + emit ERC20BridgeFinalized(localToken, remoteToken, alice, alice, 100, hex""); + + l2StandardBridge.finalizeBridgeERC20(localToken, remoteToken, alice, alice, 100, hex""); + } + + function test_finalizeBridgeERC20_isNotCorrectTokenPair_reverts() external { + address messenger = address(l2StandardBridge.messenger()); + address localToken = address(L2Token); + address remoteToken = address(BadL1Token); + vm.mockCall( + messenger, + abi.encodeCall(ICrossDomainMessenger.xDomainMessageSender, ()), + abi.encode(address(l2StandardBridge.OTHER_BRIDGE())) + ); + deal(localToken, messenger, 100, true); + vm.prank(messenger); + + vm.expectRevert("StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"); + l2StandardBridge.finalizeBridgeERC20(localToken, remoteToken, alice, alice, 100, hex""); + } +} From be8c333344e78b33eb417781b66ae6ba8a461a7e Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 15 Nov 2024 12:49:01 +0100 Subject: [PATCH 183/451] improve tests (#12935) --- .../universal/OptimismMintableERC721.t.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol index d22e0957a7df..dfe2234e0b64 100644 --- a/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol +++ b/packages/contracts-bedrock/test/universal/OptimismMintableERC721.t.sol @@ -30,6 +30,7 @@ contract OptimismMintableERC721_Test is CommonTest { vm.label(address(L2NFT), "L2ERC721Token"); } + /// @notice Tests that the constructor works as expected. function test_constructor_succeeds() external view { assertEq(L2NFT.name(), "L2NFT"); assertEq(L2NFT.symbol(), "L2T"); @@ -41,6 +42,24 @@ contract OptimismMintableERC721_Test is CommonTest { assertEq(L2NFT.REMOTE_CHAIN_ID(), 1); } + /// @notice Tests that the bridge cannot be address(0) at construction time. + function test_constructor_bridgeAsAddress0_reverts() external { + vm.expectRevert("OptimismMintableERC721: bridge cannot be address(0)"); + L2NFT = new OptimismMintableERC721(address(0), 1, address(L1NFT), "L2NFT", "L2T"); + } + + /// @notice Tests that the remote chain ID cannot be zero at construction time. + function test_constructor_remoteChainId0_reverts() external { + vm.expectRevert("OptimismMintableERC721: remote chain id cannot be zero"); + L2NFT = new OptimismMintableERC721(address(l2ERC721Bridge), 0, address(L1NFT), "L2NFT", "L2T"); + } + + /// @notice Tests that the remote token cannot be address(0) at construction time. + function test_constructor_remoteTokenAsAddress0_reverts() external { + vm.expectRevert("OptimismMintableERC721: remote token cannot be address(0)"); + L2NFT = new OptimismMintableERC721(address(l2ERC721Bridge), 1, address(0), "L2NFT", "L2T"); + } + /// @notice Ensure that the contract supports the expected interfaces. function test_supportsInterfaces_succeeds() external view { // Checks if the contract supports the IOptimismMintableERC721 interface. From 2a40d226aceaa55536344cf27f1ac603cf974c86 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 15 Nov 2024 15:30:07 +0100 Subject: [PATCH 184/451] improve tests (#12937) --- packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol | 8 ++++++++ packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol index 3949bf30f4eb..fd9d0d89b0f1 100644 --- a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol @@ -237,6 +237,14 @@ contract L1ERC721Bridge_Test is CommonTest { assertEq(localToken.ownerOf(tokenId), alice); } + /// @dev Tests that `bridgeERC721To` reverts if the to address is the zero address. + function test_bridgeERC721To_toZeroAddress_reverts() external { + // Bridge the token. + vm.prank(bob); + vm.expectRevert("ERC721Bridge: nft recipient cannot be address(0)"); + l1ERC721Bridge.bridgeERC721To(address(localToken), address(remoteToken), address(0), tokenId, 1234, hex"5678"); + } + /// @dev Tests that the ERC721 bridge successfully finalizes a withdrawal. function test_finalizeBridgeERC721_succeeds() external { // Bridge the token. diff --git a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol index 5e7798824c8d..b614711d101a 100644 --- a/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ERC721Bridge.t.sol @@ -227,6 +227,14 @@ contract L2ERC721Bridge_Test is CommonTest { assertEq(localToken.ownerOf(tokenId), alice); } + /// @dev Tests that `bridgeERC721To` reverts if the to address is the zero address. + function test_bridgeERC721To_toZeroAddress_reverts() external { + // Bridge the token. + vm.prank(bob); + vm.expectRevert("ERC721Bridge: nft recipient cannot be address(0)"); + l2ERC721Bridge.bridgeERC721To(address(localToken), address(remoteToken), address(0), tokenId, 1234, hex"5678"); + } + /// @dev Tests that `finalizeBridgeERC721` correctly finalizes a bridged token. function test_finalizeBridgeERC721_succeeds() external { // Bridge the token. From 1a067802bbd753d09bfb0b9ee1ee6e51cb7366ce Mon Sep 17 00:00:00 2001 From: mbaxter Date: Fri, 15 Nov 2024 10:17:17 -0500 Subject: [PATCH 185/451] cannon: Expand state codec tests (#12908) * cannon: Add version 3 state and enable detect test * cannon: Get other DetectVersion tests running --- cannon/mipsevm/versions/detect_test.go | 55 ++++++++++-------- .../mipsevm/versions/testdata/states/3.bin.gz | Bin 0 -> 45 bytes 2 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 cannon/mipsevm/versions/testdata/states/3.bin.gz diff --git a/cannon/mipsevm/versions/detect_test.go b/cannon/mipsevm/versions/detect_test.go index be849269fff9..bd1acd115eaf 100644 --- a/cannon/mipsevm/versions/detect_test.go +++ b/cannon/mipsevm/versions/detect_test.go @@ -7,10 +7,12 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/op-service/ioutil" - "github.com/stretchr/testify/require" ) const statesPath = "testdata/states" @@ -18,7 +20,7 @@ const statesPath = "testdata/states" //go:embed testdata/states var historicStates embed.FS -func TestDetectVersion(t *testing.T) { +func TestDetectVersion_fromFile(t *testing.T) { testDetection := func(t *testing.T, version StateVersion, ext string) { filename := strconv.Itoa(int(version)) + ext dir := t.TempDir() @@ -34,9 +36,6 @@ func TestDetectVersion(t *testing.T) { // Iterate all known versions to ensure we have a test case to detect every state version for _, version := range StateVersionTypes { version := version - if version == VersionMultiThreaded64 { - t.Skip("TODO(#12205)") - } t.Run(version.String(), func(t *testing.T) { testDetection(t, version, ".bin.gz") }) @@ -47,28 +46,38 @@ func TestDetectVersion(t *testing.T) { }) } } +} - // Additionally, check that the latest supported versions write new states in a way that is detected correctly - t.Run("SingleThreadedBinary", func(t *testing.T) { - state, err := NewFromState(singlethreaded.CreateEmptyState()) - require.NoError(t, err) - path := writeToFile(t, "state.bin.gz", state) - version, err := DetectVersion(path) - require.NoError(t, err) - require.Equal(t, VersionSingleThreaded2, version) - }) +// Check that the latest supported versions write new states in a way that is detected correctly +func TestDetectVersion_singleThreadedBinary(t *testing.T) { + targetVersion := VersionSingleThreaded2 + if !arch.IsMips32 { + t.Skip("Single-threaded states are not supported for 64-bit VMs") + } - t.Run("MultiThreadedBinary", func(t *testing.T) { - state, err := NewFromState(multithreaded.CreateEmptyState()) - require.NoError(t, err) - path := writeToFile(t, "state.bin.gz", state) - version, err := DetectVersion(path) - require.NoError(t, err) - require.Equal(t, VersionMultiThreaded, version) - }) + state, err := NewFromState(singlethreaded.CreateEmptyState()) + require.NoError(t, err) + path := writeToFile(t, "state.bin.gz", state) + version, err := DetectVersion(path) + require.NoError(t, err) + require.Equal(t, targetVersion, version) +} + +func TestDetectVersion_multiThreadedBinary(t *testing.T) { + targetVersion := VersionMultiThreaded + if !arch.IsMips32 { + targetVersion = VersionMultiThreaded64 + } + + state, err := NewFromState(multithreaded.CreateEmptyState()) + require.NoError(t, err) + path := writeToFile(t, "state.bin.gz", state) + version, err := DetectVersion(path) + require.NoError(t, err) + require.Equal(t, targetVersion, version) } -func TestDetectVersionInvalid(t *testing.T) { +func TestDetectVersion_invalid(t *testing.T) { t.Run("bad gzip", func(t *testing.T) { dir := t.TempDir() filename := "state.bin.gz" diff --git a/cannon/mipsevm/versions/testdata/states/3.bin.gz b/cannon/mipsevm/versions/testdata/states/3.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..b6f6728313a7d5f908724a9674dd68549270c623 GIT binary patch literal 45 wcmb2|=3oGW|4C^JWPk9p8rUgI^}2HM9dJ?>=i_Do%KrcV;pa7$!;B0J006)Yxc~qF literal 0 HcmV?d00001 From b01b93eeb1191f575777280b86e6f6fb6cda577c Mon Sep 17 00:00:00 2001 From: Maurelian Date: Sat, 16 Nov 2024 00:10:56 +0700 Subject: [PATCH 186/451] feat: Remove DelayedVetoable (#12939) --- op-e2e/bindings/delayedvetoable.go | 928 ------------------ .../snapshots/abi/DelayedVetoable.json | 207 ---- .../snapshots/semver-lock.json | 4 - .../storageLayout/DelayedVetoable.json | 16 - .../src/L1/DelayedVetoable.sol | 193 ---- .../src/L1/interfaces/IDelayedVetoable.sol | 23 - .../test/L1/DelayedVetoable.t.sol | 262 ----- .../test/universal/Specs.t.sol | 8 - 8 files changed, 1641 deletions(-) delete mode 100644 op-e2e/bindings/delayedvetoable.go delete mode 100644 packages/contracts-bedrock/snapshots/abi/DelayedVetoable.json delete mode 100644 packages/contracts-bedrock/snapshots/storageLayout/DelayedVetoable.json delete mode 100644 packages/contracts-bedrock/src/L1/DelayedVetoable.sol delete mode 100644 packages/contracts-bedrock/src/L1/interfaces/IDelayedVetoable.sol delete mode 100644 packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol diff --git a/op-e2e/bindings/delayedvetoable.go b/op-e2e/bindings/delayedvetoable.go deleted file mode 100644 index 989bb0278d20..000000000000 --- a/op-e2e/bindings/delayedvetoable.go +++ /dev/null @@ -1,928 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package bindings - -import ( - "errors" - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription -) - -// DelayedVetoableMetaData contains all meta data concerning the DelayedVetoable contract. -var DelayedVetoableMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"vetoer_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"initiator_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"target_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"operatingDelay_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"fallback\",\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"delay\",\"inputs\":[],\"outputs\":[{\"name\":\"delay_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"initiator\",\"inputs\":[],\"outputs\":[{\"name\":\"initiator_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"queuedAt\",\"inputs\":[{\"name\":\"callHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"queuedAt_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"target\",\"inputs\":[],\"outputs\":[{\"name\":\"target_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"vetoer\",\"inputs\":[],\"outputs\":[{\"name\":\"vetoer_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"DelayActivated\",\"inputs\":[{\"name\":\"delay\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Forwarded\",\"inputs\":[{\"name\":\"callHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initiated\",\"inputs\":[{\"name\":\"callHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Vetoed\",\"inputs\":[{\"name\":\"callHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"data\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ForwardingEarly\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"Unauthorized\",\"inputs\":[{\"name\":\"expected\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"actual\",\"type\":\"address\",\"internalType\":\"address\"}]}]", - Bin: "0x61010060405234801561001157600080fd5b506040516108ff3803806108ff8339810160408190526100309161006e565b6001600160a01b0393841660a05291831660c05290911660805260e0526100b9565b80516001600160a01b038116811461006957600080fd5b919050565b6000806000806080858703121561008457600080fd5b61008d85610052565b935061009b60208601610052565b92506100a960408601610052565b6060959095015193969295505050565b60805160a05160c05160e0516107dc610123600039600061023f01526000818161015f01528181610205015281816102cd0152818161045801526105050152600081816101a001528181610384015261059d01526000818161057101526105ff01526107dc6000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063b912de5d11610050578063b912de5d14610111578063d4b8399214610124578063d8bff4401461012c57610072565b806354fd4d501461007c5780635c39fcc1146100ce5780636a42b8f8146100fb575b61007a610134565b005b6100b86040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516100c591906106a7565b60405180910390f35b6100d66104fb565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100c5565b610103610532565b6040519081526020016100c5565b61010361011f36600461071a565b610540565b6100d6610567565b6100d6610593565b361580156101425750600054155b15610298573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016148015906101c357503373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614155b1561023d576040517f295a81c100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660048201523360248201526044015b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000060008190556040519081527febf28bfb587e28dfffd9173cf71c32ba5d3f0544a0117b5539c9b274a5bba2a89060200160405180910390a1565b600080366040516102aa929190610733565b60405190819003902090503373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161480156103065750600081815260016020526040902054155b1561036c5760005460000361031e5761031e816105bf565b6000818152600160205260408082204290555182917f87a332a414acbc7da074543639ce7ae02ff1ea72e88379da9f261b080beb5a139161036191903690610743565b60405180910390a250565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161480156103be575060008181526001602052604090205415155b15610406576000818152600160205260408082208290555182917fbede6852c1d97d93ff557f676de76670cd0dec861e7fe8beb13aa0ba2b0ab0409161036191903690610743565b600081815260016020526040812054900361048b576040517f295a81c100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166004820152336024820152604401610234565b60008054828252600160205260409091205442916104a891610790565b11156104e0576040517f43dc986d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600160205260408120556104f8816105bf565b50565b60003361052757507f000000000000000000000000000000000000000000000000000000000000000090565b61052f610134565b90565b600033610527575060005490565b60003361055a575060009081526001602052604090205490565b610562610134565b919050565b60003361052757507f000000000000000000000000000000000000000000000000000000000000000090565b60003361052757507f000000000000000000000000000000000000000000000000000000000000000090565b807f4c109d85bcd0bb5c735b4be850953d652afe4cd9aa2e0b1426a65a4dcb2e12296000366040516105f2929190610743565b60405180910390a26000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16600036604051610645929190610733565b6000604051808303816000865af19150503d8060008114610682576040519150601f19603f3d011682016040523d82523d6000602084013e610687565b606091505b50909250905081151560010361069f57805160208201f35b805160208201fd5b600060208083528351808285015260005b818110156106d4578581018301518582016040015282016106b8565b818111156106e6576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006020828403121561072c57600080fd5b5035919050565b8183823760009101908152919050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600082198211156107ca577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50019056fea164736f6c634300080f000a", -} - -// DelayedVetoableABI is the input ABI used to generate the binding from. -// Deprecated: Use DelayedVetoableMetaData.ABI instead. -var DelayedVetoableABI = DelayedVetoableMetaData.ABI - -// DelayedVetoableBin is the compiled bytecode used for deploying new contracts. -// Deprecated: Use DelayedVetoableMetaData.Bin instead. -var DelayedVetoableBin = DelayedVetoableMetaData.Bin - -// DeployDelayedVetoable deploys a new Ethereum contract, binding an instance of DelayedVetoable to it. -func DeployDelayedVetoable(auth *bind.TransactOpts, backend bind.ContractBackend, vetoer_ common.Address, initiator_ common.Address, target_ common.Address, operatingDelay_ *big.Int) (common.Address, *types.Transaction, *DelayedVetoable, error) { - parsed, err := DelayedVetoableMetaData.GetAbi() - if err != nil { - return common.Address{}, nil, nil, err - } - if parsed == nil { - return common.Address{}, nil, nil, errors.New("GetABI returned nil") - } - - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DelayedVetoableBin), backend, vetoer_, initiator_, target_, operatingDelay_) - if err != nil { - return common.Address{}, nil, nil, err - } - return address, tx, &DelayedVetoable{DelayedVetoableCaller: DelayedVetoableCaller{contract: contract}, DelayedVetoableTransactor: DelayedVetoableTransactor{contract: contract}, DelayedVetoableFilterer: DelayedVetoableFilterer{contract: contract}}, nil -} - -// DelayedVetoable is an auto generated Go binding around an Ethereum contract. -type DelayedVetoable struct { - DelayedVetoableCaller // Read-only binding to the contract - DelayedVetoableTransactor // Write-only binding to the contract - DelayedVetoableFilterer // Log filterer for contract events -} - -// DelayedVetoableCaller is an auto generated read-only Go binding around an Ethereum contract. -type DelayedVetoableCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// DelayedVetoableTransactor is an auto generated write-only Go binding around an Ethereum contract. -type DelayedVetoableTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// DelayedVetoableFilterer is an auto generated log filtering Go binding around an Ethereum contract events. -type DelayedVetoableFilterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// DelayedVetoableSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type DelayedVetoableSession struct { - Contract *DelayedVetoable // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// DelayedVetoableCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type DelayedVetoableCallerSession struct { - Contract *DelayedVetoableCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// DelayedVetoableTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type DelayedVetoableTransactorSession struct { - Contract *DelayedVetoableTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// DelayedVetoableRaw is an auto generated low-level Go binding around an Ethereum contract. -type DelayedVetoableRaw struct { - Contract *DelayedVetoable // Generic contract binding to access the raw methods on -} - -// DelayedVetoableCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type DelayedVetoableCallerRaw struct { - Contract *DelayedVetoableCaller // Generic read-only contract binding to access the raw methods on -} - -// DelayedVetoableTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type DelayedVetoableTransactorRaw struct { - Contract *DelayedVetoableTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewDelayedVetoable creates a new instance of DelayedVetoable, bound to a specific deployed contract. -func NewDelayedVetoable(address common.Address, backend bind.ContractBackend) (*DelayedVetoable, error) { - contract, err := bindDelayedVetoable(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &DelayedVetoable{DelayedVetoableCaller: DelayedVetoableCaller{contract: contract}, DelayedVetoableTransactor: DelayedVetoableTransactor{contract: contract}, DelayedVetoableFilterer: DelayedVetoableFilterer{contract: contract}}, nil -} - -// NewDelayedVetoableCaller creates a new read-only instance of DelayedVetoable, bound to a specific deployed contract. -func NewDelayedVetoableCaller(address common.Address, caller bind.ContractCaller) (*DelayedVetoableCaller, error) { - contract, err := bindDelayedVetoable(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &DelayedVetoableCaller{contract: contract}, nil -} - -// NewDelayedVetoableTransactor creates a new write-only instance of DelayedVetoable, bound to a specific deployed contract. -func NewDelayedVetoableTransactor(address common.Address, transactor bind.ContractTransactor) (*DelayedVetoableTransactor, error) { - contract, err := bindDelayedVetoable(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &DelayedVetoableTransactor{contract: contract}, nil -} - -// NewDelayedVetoableFilterer creates a new log filterer instance of DelayedVetoable, bound to a specific deployed contract. -func NewDelayedVetoableFilterer(address common.Address, filterer bind.ContractFilterer) (*DelayedVetoableFilterer, error) { - contract, err := bindDelayedVetoable(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &DelayedVetoableFilterer{contract: contract}, nil -} - -// bindDelayedVetoable binds a generic wrapper to an already deployed contract. -func bindDelayedVetoable(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(DelayedVetoableABI)) - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_DelayedVetoable *DelayedVetoableRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _DelayedVetoable.Contract.DelayedVetoableCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_DelayedVetoable *DelayedVetoableRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.Contract.DelayedVetoableTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_DelayedVetoable *DelayedVetoableRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _DelayedVetoable.Contract.DelayedVetoableTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_DelayedVetoable *DelayedVetoableCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _DelayedVetoable.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_DelayedVetoable *DelayedVetoableTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_DelayedVetoable *DelayedVetoableTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _DelayedVetoable.Contract.contract.Transact(opts, method, params...) -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_DelayedVetoable *DelayedVetoableCaller) Version(opts *bind.CallOpts) (string, error) { - var out []interface{} - err := _DelayedVetoable.contract.Call(opts, &out, "version") - - if err != nil { - return *new(string), err - } - - out0 := *abi.ConvertType(out[0], new(string)).(*string) - - return out0, err - -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_DelayedVetoable *DelayedVetoableSession) Version() (string, error) { - return _DelayedVetoable.Contract.Version(&_DelayedVetoable.CallOpts) -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_DelayedVetoable *DelayedVetoableCallerSession) Version() (string, error) { - return _DelayedVetoable.Contract.Version(&_DelayedVetoable.CallOpts) -} - -// Delay is a paid mutator transaction binding the contract method 0x6a42b8f8. -// -// Solidity: function delay() returns(uint256 delay_) -func (_DelayedVetoable *DelayedVetoableTransactor) Delay(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.contract.Transact(opts, "delay") -} - -// Delay is a paid mutator transaction binding the contract method 0x6a42b8f8. -// -// Solidity: function delay() returns(uint256 delay_) -func (_DelayedVetoable *DelayedVetoableSession) Delay() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Delay(&_DelayedVetoable.TransactOpts) -} - -// Delay is a paid mutator transaction binding the contract method 0x6a42b8f8. -// -// Solidity: function delay() returns(uint256 delay_) -func (_DelayedVetoable *DelayedVetoableTransactorSession) Delay() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Delay(&_DelayedVetoable.TransactOpts) -} - -// Initiator is a paid mutator transaction binding the contract method 0x5c39fcc1. -// -// Solidity: function initiator() returns(address initiator_) -func (_DelayedVetoable *DelayedVetoableTransactor) Initiator(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.contract.Transact(opts, "initiator") -} - -// Initiator is a paid mutator transaction binding the contract method 0x5c39fcc1. -// -// Solidity: function initiator() returns(address initiator_) -func (_DelayedVetoable *DelayedVetoableSession) Initiator() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Initiator(&_DelayedVetoable.TransactOpts) -} - -// Initiator is a paid mutator transaction binding the contract method 0x5c39fcc1. -// -// Solidity: function initiator() returns(address initiator_) -func (_DelayedVetoable *DelayedVetoableTransactorSession) Initiator() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Initiator(&_DelayedVetoable.TransactOpts) -} - -// QueuedAt is a paid mutator transaction binding the contract method 0xb912de5d. -// -// Solidity: function queuedAt(bytes32 callHash) returns(uint256 queuedAt_) -func (_DelayedVetoable *DelayedVetoableTransactor) QueuedAt(opts *bind.TransactOpts, callHash [32]byte) (*types.Transaction, error) { - return _DelayedVetoable.contract.Transact(opts, "queuedAt", callHash) -} - -// QueuedAt is a paid mutator transaction binding the contract method 0xb912de5d. -// -// Solidity: function queuedAt(bytes32 callHash) returns(uint256 queuedAt_) -func (_DelayedVetoable *DelayedVetoableSession) QueuedAt(callHash [32]byte) (*types.Transaction, error) { - return _DelayedVetoable.Contract.QueuedAt(&_DelayedVetoable.TransactOpts, callHash) -} - -// QueuedAt is a paid mutator transaction binding the contract method 0xb912de5d. -// -// Solidity: function queuedAt(bytes32 callHash) returns(uint256 queuedAt_) -func (_DelayedVetoable *DelayedVetoableTransactorSession) QueuedAt(callHash [32]byte) (*types.Transaction, error) { - return _DelayedVetoable.Contract.QueuedAt(&_DelayedVetoable.TransactOpts, callHash) -} - -// Target is a paid mutator transaction binding the contract method 0xd4b83992. -// -// Solidity: function target() returns(address target_) -func (_DelayedVetoable *DelayedVetoableTransactor) Target(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.contract.Transact(opts, "target") -} - -// Target is a paid mutator transaction binding the contract method 0xd4b83992. -// -// Solidity: function target() returns(address target_) -func (_DelayedVetoable *DelayedVetoableSession) Target() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Target(&_DelayedVetoable.TransactOpts) -} - -// Target is a paid mutator transaction binding the contract method 0xd4b83992. -// -// Solidity: function target() returns(address target_) -func (_DelayedVetoable *DelayedVetoableTransactorSession) Target() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Target(&_DelayedVetoable.TransactOpts) -} - -// Vetoer is a paid mutator transaction binding the contract method 0xd8bff440. -// -// Solidity: function vetoer() returns(address vetoer_) -func (_DelayedVetoable *DelayedVetoableTransactor) Vetoer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _DelayedVetoable.contract.Transact(opts, "vetoer") -} - -// Vetoer is a paid mutator transaction binding the contract method 0xd8bff440. -// -// Solidity: function vetoer() returns(address vetoer_) -func (_DelayedVetoable *DelayedVetoableSession) Vetoer() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Vetoer(&_DelayedVetoable.TransactOpts) -} - -// Vetoer is a paid mutator transaction binding the contract method 0xd8bff440. -// -// Solidity: function vetoer() returns(address vetoer_) -func (_DelayedVetoable *DelayedVetoableTransactorSession) Vetoer() (*types.Transaction, error) { - return _DelayedVetoable.Contract.Vetoer(&_DelayedVetoable.TransactOpts) -} - -// Fallback is a paid mutator transaction binding the contract fallback function. -// -// Solidity: fallback() returns() -func (_DelayedVetoable *DelayedVetoableTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { - return _DelayedVetoable.contract.RawTransact(opts, calldata) -} - -// Fallback is a paid mutator transaction binding the contract fallback function. -// -// Solidity: fallback() returns() -func (_DelayedVetoable *DelayedVetoableSession) Fallback(calldata []byte) (*types.Transaction, error) { - return _DelayedVetoable.Contract.Fallback(&_DelayedVetoable.TransactOpts, calldata) -} - -// Fallback is a paid mutator transaction binding the contract fallback function. -// -// Solidity: fallback() returns() -func (_DelayedVetoable *DelayedVetoableTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { - return _DelayedVetoable.Contract.Fallback(&_DelayedVetoable.TransactOpts, calldata) -} - -// DelayedVetoableDelayActivatedIterator is returned from FilterDelayActivated and is used to iterate over the raw logs and unpacked data for DelayActivated events raised by the DelayedVetoable contract. -type DelayedVetoableDelayActivatedIterator struct { - Event *DelayedVetoableDelayActivated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *DelayedVetoableDelayActivatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableDelayActivated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableDelayActivated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *DelayedVetoableDelayActivatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *DelayedVetoableDelayActivatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// DelayedVetoableDelayActivated represents a DelayActivated event raised by the DelayedVetoable contract. -type DelayedVetoableDelayActivated struct { - Delay *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterDelayActivated is a free log retrieval operation binding the contract event 0xebf28bfb587e28dfffd9173cf71c32ba5d3f0544a0117b5539c9b274a5bba2a8. -// -// Solidity: event DelayActivated(uint256 delay) -func (_DelayedVetoable *DelayedVetoableFilterer) FilterDelayActivated(opts *bind.FilterOpts) (*DelayedVetoableDelayActivatedIterator, error) { - - logs, sub, err := _DelayedVetoable.contract.FilterLogs(opts, "DelayActivated") - if err != nil { - return nil, err - } - return &DelayedVetoableDelayActivatedIterator{contract: _DelayedVetoable.contract, event: "DelayActivated", logs: logs, sub: sub}, nil -} - -// WatchDelayActivated is a free log subscription operation binding the contract event 0xebf28bfb587e28dfffd9173cf71c32ba5d3f0544a0117b5539c9b274a5bba2a8. -// -// Solidity: event DelayActivated(uint256 delay) -func (_DelayedVetoable *DelayedVetoableFilterer) WatchDelayActivated(opts *bind.WatchOpts, sink chan<- *DelayedVetoableDelayActivated) (event.Subscription, error) { - - logs, sub, err := _DelayedVetoable.contract.WatchLogs(opts, "DelayActivated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(DelayedVetoableDelayActivated) - if err := _DelayedVetoable.contract.UnpackLog(event, "DelayActivated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseDelayActivated is a log parse operation binding the contract event 0xebf28bfb587e28dfffd9173cf71c32ba5d3f0544a0117b5539c9b274a5bba2a8. -// -// Solidity: event DelayActivated(uint256 delay) -func (_DelayedVetoable *DelayedVetoableFilterer) ParseDelayActivated(log types.Log) (*DelayedVetoableDelayActivated, error) { - event := new(DelayedVetoableDelayActivated) - if err := _DelayedVetoable.contract.UnpackLog(event, "DelayActivated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// DelayedVetoableForwardedIterator is returned from FilterForwarded and is used to iterate over the raw logs and unpacked data for Forwarded events raised by the DelayedVetoable contract. -type DelayedVetoableForwardedIterator struct { - Event *DelayedVetoableForwarded // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *DelayedVetoableForwardedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableForwarded) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableForwarded) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *DelayedVetoableForwardedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *DelayedVetoableForwardedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// DelayedVetoableForwarded represents a Forwarded event raised by the DelayedVetoable contract. -type DelayedVetoableForwarded struct { - CallHash [32]byte - Data []byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterForwarded is a free log retrieval operation binding the contract event 0x4c109d85bcd0bb5c735b4be850953d652afe4cd9aa2e0b1426a65a4dcb2e1229. -// -// Solidity: event Forwarded(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) FilterForwarded(opts *bind.FilterOpts, callHash [][32]byte) (*DelayedVetoableForwardedIterator, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.FilterLogs(opts, "Forwarded", callHashRule) - if err != nil { - return nil, err - } - return &DelayedVetoableForwardedIterator{contract: _DelayedVetoable.contract, event: "Forwarded", logs: logs, sub: sub}, nil -} - -// WatchForwarded is a free log subscription operation binding the contract event 0x4c109d85bcd0bb5c735b4be850953d652afe4cd9aa2e0b1426a65a4dcb2e1229. -// -// Solidity: event Forwarded(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) WatchForwarded(opts *bind.WatchOpts, sink chan<- *DelayedVetoableForwarded, callHash [][32]byte) (event.Subscription, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.WatchLogs(opts, "Forwarded", callHashRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(DelayedVetoableForwarded) - if err := _DelayedVetoable.contract.UnpackLog(event, "Forwarded", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseForwarded is a log parse operation binding the contract event 0x4c109d85bcd0bb5c735b4be850953d652afe4cd9aa2e0b1426a65a4dcb2e1229. -// -// Solidity: event Forwarded(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) ParseForwarded(log types.Log) (*DelayedVetoableForwarded, error) { - event := new(DelayedVetoableForwarded) - if err := _DelayedVetoable.contract.UnpackLog(event, "Forwarded", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// DelayedVetoableInitiatedIterator is returned from FilterInitiated and is used to iterate over the raw logs and unpacked data for Initiated events raised by the DelayedVetoable contract. -type DelayedVetoableInitiatedIterator struct { - Event *DelayedVetoableInitiated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *DelayedVetoableInitiatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableInitiated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableInitiated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *DelayedVetoableInitiatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *DelayedVetoableInitiatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// DelayedVetoableInitiated represents a Initiated event raised by the DelayedVetoable contract. -type DelayedVetoableInitiated struct { - CallHash [32]byte - Data []byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterInitiated is a free log retrieval operation binding the contract event 0x87a332a414acbc7da074543639ce7ae02ff1ea72e88379da9f261b080beb5a13. -// -// Solidity: event Initiated(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) FilterInitiated(opts *bind.FilterOpts, callHash [][32]byte) (*DelayedVetoableInitiatedIterator, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.FilterLogs(opts, "Initiated", callHashRule) - if err != nil { - return nil, err - } - return &DelayedVetoableInitiatedIterator{contract: _DelayedVetoable.contract, event: "Initiated", logs: logs, sub: sub}, nil -} - -// WatchInitiated is a free log subscription operation binding the contract event 0x87a332a414acbc7da074543639ce7ae02ff1ea72e88379da9f261b080beb5a13. -// -// Solidity: event Initiated(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) WatchInitiated(opts *bind.WatchOpts, sink chan<- *DelayedVetoableInitiated, callHash [][32]byte) (event.Subscription, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.WatchLogs(opts, "Initiated", callHashRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(DelayedVetoableInitiated) - if err := _DelayedVetoable.contract.UnpackLog(event, "Initiated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseInitiated is a log parse operation binding the contract event 0x87a332a414acbc7da074543639ce7ae02ff1ea72e88379da9f261b080beb5a13. -// -// Solidity: event Initiated(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) ParseInitiated(log types.Log) (*DelayedVetoableInitiated, error) { - event := new(DelayedVetoableInitiated) - if err := _DelayedVetoable.contract.UnpackLog(event, "Initiated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// DelayedVetoableVetoedIterator is returned from FilterVetoed and is used to iterate over the raw logs and unpacked data for Vetoed events raised by the DelayedVetoable contract. -type DelayedVetoableVetoedIterator struct { - Event *DelayedVetoableVetoed // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *DelayedVetoableVetoedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableVetoed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(DelayedVetoableVetoed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *DelayedVetoableVetoedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *DelayedVetoableVetoedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// DelayedVetoableVetoed represents a Vetoed event raised by the DelayedVetoable contract. -type DelayedVetoableVetoed struct { - CallHash [32]byte - Data []byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterVetoed is a free log retrieval operation binding the contract event 0xbede6852c1d97d93ff557f676de76670cd0dec861e7fe8beb13aa0ba2b0ab040. -// -// Solidity: event Vetoed(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) FilterVetoed(opts *bind.FilterOpts, callHash [][32]byte) (*DelayedVetoableVetoedIterator, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.FilterLogs(opts, "Vetoed", callHashRule) - if err != nil { - return nil, err - } - return &DelayedVetoableVetoedIterator{contract: _DelayedVetoable.contract, event: "Vetoed", logs: logs, sub: sub}, nil -} - -// WatchVetoed is a free log subscription operation binding the contract event 0xbede6852c1d97d93ff557f676de76670cd0dec861e7fe8beb13aa0ba2b0ab040. -// -// Solidity: event Vetoed(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) WatchVetoed(opts *bind.WatchOpts, sink chan<- *DelayedVetoableVetoed, callHash [][32]byte) (event.Subscription, error) { - - var callHashRule []interface{} - for _, callHashItem := range callHash { - callHashRule = append(callHashRule, callHashItem) - } - - logs, sub, err := _DelayedVetoable.contract.WatchLogs(opts, "Vetoed", callHashRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(DelayedVetoableVetoed) - if err := _DelayedVetoable.contract.UnpackLog(event, "Vetoed", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseVetoed is a log parse operation binding the contract event 0xbede6852c1d97d93ff557f676de76670cd0dec861e7fe8beb13aa0ba2b0ab040. -// -// Solidity: event Vetoed(bytes32 indexed callHash, bytes data) -func (_DelayedVetoable *DelayedVetoableFilterer) ParseVetoed(log types.Log) (*DelayedVetoableVetoed, error) { - event := new(DelayedVetoableVetoed) - if err := _DelayedVetoable.contract.UnpackLog(event, "Vetoed", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} diff --git a/packages/contracts-bedrock/snapshots/abi/DelayedVetoable.json b/packages/contracts-bedrock/snapshots/abi/DelayedVetoable.json deleted file mode 100644 index d76d1c8b108b..000000000000 --- a/packages/contracts-bedrock/snapshots/abi/DelayedVetoable.json +++ /dev/null @@ -1,207 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_vetoer", - "type": "address" - }, - { - "internalType": "address", - "name": "_initiator", - "type": "address" - }, - { - "internalType": "address", - "name": "_target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_operatingDelay", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "stateMutability": "nonpayable", - "type": "fallback" - }, - { - "inputs": [], - "name": "delay", - "outputs": [ - { - "internalType": "uint256", - "name": "delay_", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "initiator", - "outputs": [ - { - "internalType": "address", - "name": "initiator_", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_callHash", - "type": "bytes32" - } - ], - "name": "queuedAt", - "outputs": [ - { - "internalType": "uint256", - "name": "queuedAt_", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "target", - "outputs": [ - { - "internalType": "address", - "name": "target_", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "vetoer", - "outputs": [ - { - "internalType": "address", - "name": "vetoer_", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "delay", - "type": "uint256" - } - ], - "name": "DelayActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "callHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "Forwarded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "callHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "Initiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "callHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "Vetoed", - "type": "event" - }, - { - "inputs": [], - "name": "ForwardingEarly", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "expected", - "type": "address" - }, - { - "internalType": "address", - "name": "actual", - "type": "address" - } - ], - "name": "Unauthorized", - "type": "error" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index fb7b6d824392..e96fa687e9c6 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -3,10 +3,6 @@ "initCodeHash": "0xbd00d6568abab3e7fc211c40d682862242f25493010a4a097bd1f3b45c8c87c3", "sourceCodeHash": "0x58b587034a67b4bb718abbaded8ac23b082c0971105874bcc42c23f051c67f6e" }, - "src/L1/DelayedVetoable.sol": { - "initCodeHash": "0x9fe8ade6f6332262ff1f3539ac0bf57660edbad3cf4c4cb230c2ddac18aa0a3f", - "sourceCodeHash": "0x30e83a535ef27b2e900c831c4e1a4ec2750195350011c4fdacda1da9db2d167b" - }, "src/L1/L1CrossDomainMessenger.sol": { "initCodeHash": "0x2e9cb3ceb5e55341b311f0666ef7655df4fafae75afdfbcd701cd9c9b2b017d5", "sourceCodeHash": "0x848ec3774be17bcc8ba65a23d08e35e979b3f39f9d2ac8a810188f945c69c9ea" diff --git a/packages/contracts-bedrock/snapshots/storageLayout/DelayedVetoable.json b/packages/contracts-bedrock/snapshots/storageLayout/DelayedVetoable.json deleted file mode 100644 index 7da3cbbe5bd6..000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/DelayedVetoable.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "bytes": "32", - "label": "_delay", - "offset": 0, - "slot": "0", - "type": "uint256" - }, - { - "bytes": "32", - "label": "_queuedAt", - "offset": 0, - "slot": "1", - "type": "mapping(bytes32 => uint256)" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/DelayedVetoable.sol b/packages/contracts-bedrock/src/L1/DelayedVetoable.sol deleted file mode 100644 index d968af214975..000000000000 --- a/packages/contracts-bedrock/src/L1/DelayedVetoable.sol +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Interfaces -import { ISemver } from "src/universal/interfaces/ISemver.sol"; - -/// @title DelayedVetoable -/// @notice This contract enables a delay before a call is forwarded to a target contract, and during the delay period -/// the call can be vetoed by the authorized vetoer. -/// This contract does not support value transfers, only data is forwarded. -/// Additionally, this contract cannot be used to forward calls with data beginning with the function selector -/// of the queuedAt(bytes32) function. This is because of input validation checks which solidity performs at -/// runtime on functions which take an argument. -contract DelayedVetoable is ISemver { - /// @notice Error for when attempting to forward too early. - error ForwardingEarly(); - - /// @notice Error for unauthorized calls. - error Unauthorized(address expected, address actual); - - /// @notice An event that is emitted when the delay is activated. - /// @param delay The delay that was activated. - event DelayActivated(uint256 delay); - - /// @notice An event that is emitted when a call is initiated. - /// @param callHash The hash of the call data. - /// @param data The data of the initiated call. - event Initiated(bytes32 indexed callHash, bytes data); - - /// @notice An event that is emitted each time a call is forwarded. - /// @param callHash The hash of the call data. - /// @param data The data forwarded to the target. - event Forwarded(bytes32 indexed callHash, bytes data); - - /// @notice An event that is emitted each time a call is vetoed. - /// @param callHash The hash of the call data. - /// @param data The data forwarded to the target. - event Vetoed(bytes32 indexed callHash, bytes data); - - /// @notice The address that all calls are forwarded to after the delay. - address internal immutable TARGET; - - /// @notice The address that can veto a call. - address internal immutable VETOER; - - /// @notice The address that can initiate a call. - address internal immutable INITIATOR; - - /// @notice The delay which will be set after the initial system deployment is completed. - uint256 internal immutable OPERATING_DELAY; - - /// @notice The current amount of time to wait before forwarding a call. - uint256 internal _delay; - - /// @notice The time that a call was initiated. - mapping(bytes32 => uint256) internal _queuedAt; - - /// @notice A modifier that reverts if not called by the vetoer or by address(0) to allow - /// eth_call to interact with this proxy without needing to use low-level storage - /// inspection. We assume that nobody is able to trigger calls from address(0) during - /// normal EVM execution. - modifier readOrHandle() { - if (msg.sender == address(0)) { - _; - } else { - // This WILL halt the call frame on completion. - _handleCall(); - } - } - - /// @notice Semantic version. - /// @custom:semver 1.0.1-beta.2 - string public constant version = "1.0.1-beta.2"; - - /// @notice Sets the target admin during contract deployment. - /// @param _vetoer Address of the vetoer. - /// @param _initiator Address of the initiator. - /// @param _target Address of the target. - /// @param _operatingDelay Time to delay when the system is operational. - constructor(address _vetoer, address _initiator, address _target, uint256 _operatingDelay) { - // Note that the _delay value is not set here. Having an initial delay of 0 is helpful - // during the deployment of a new system. - VETOER = _vetoer; - INITIATOR = _initiator; - TARGET = _target; - OPERATING_DELAY = _operatingDelay; - } - - /// @notice Gets the initiator - /// @return initiator_ Initiator address. - function initiator() external virtual readOrHandle returns (address initiator_) { - initiator_ = INITIATOR; - } - - //// @notice Queries the vetoer address. - /// @return vetoer_ Vetoer address. - function vetoer() external virtual readOrHandle returns (address vetoer_) { - vetoer_ = VETOER; - } - - //// @notice Queries the target address. - /// @return target_ Target address. - function target() external readOrHandle returns (address target_) { - target_ = TARGET; - } - - /// @notice Gets the delay - /// @return delay_ Delay address. - function delay() external readOrHandle returns (uint256 delay_) { - delay_ = _delay; - } - - /// @notice Gets entries in the _queuedAt mapping. - /// @param _callHash The hash of the call data. - /// @return queuedAt_ The time the callHash was recorded. - function queuedAt(bytes32 _callHash) external readOrHandle returns (uint256 queuedAt_) { - queuedAt_ = _queuedAt[_callHash]; - } - - /// @notice Used for all calls that pass data to the contract. - fallback() external { - _handleCall(); - } - - /// @notice Receives all calls other than those made by the vetoer. - /// This enables transparent initiation and forwarding of calls to the target and avoids - /// the need for additional layers of abi encoding. - function _handleCall() internal { - // The initiator and vetoer activate the delay by passing in null data. - if (msg.data.length == 0 && _delay == 0) { - if (msg.sender != INITIATOR && msg.sender != VETOER) { - revert Unauthorized(INITIATOR, msg.sender); - } - _delay = OPERATING_DELAY; - emit DelayActivated(_delay); - return; - } - - bytes32 callHash = keccak256(msg.data); - - // Case 1: The initiator is calling the contract to initiate a call. - if (msg.sender == INITIATOR && _queuedAt[callHash] == 0) { - if (_delay == 0) { - // This forward function will halt the call frame on completion. - _forwardAndHalt(callHash); - } - _queuedAt[callHash] = block.timestamp; - emit Initiated(callHash, msg.data); - return; - } - - // Case 2: The vetoer is calling the contract to veto a call. - // Note: The vetoer retains the ability to veto even after the delay has passed. This makes censoring the vetoer - // more costly, as there is no time limit after which their transaction can be included. - if (msg.sender == VETOER && _queuedAt[callHash] != 0) { - delete _queuedAt[callHash]; - emit Vetoed(callHash, msg.data); - return; - } - - // Case 3: The call is from an unpermissioned actor. We'll forward the call if the delay has - // passed. - if (_queuedAt[callHash] == 0) { - // The call has not been initiated, so we'll treat this is an unauthorized initiation attempt. - revert Unauthorized(INITIATOR, msg.sender); - } - - if (_queuedAt[callHash] + _delay > block.timestamp) { - // Not enough time has passed, so we'll revert. - revert ForwardingEarly(); - } - - // Delete the call to prevent replays - delete _queuedAt[callHash]; - _forwardAndHalt(callHash); - } - - /// @notice Forwards the call to the target and halts the call frame. - function _forwardAndHalt(bytes32 _callHash) internal { - // Forward the call - emit Forwarded(_callHash, msg.data); - (bool success, bytes memory returndata) = TARGET.call(msg.data); - if (success == true) { - assembly { - return(add(returndata, 0x20), mload(returndata)) - } - } else { - assembly { - revert(add(returndata, 0x20), mload(returndata)) - } - } - } -} diff --git a/packages/contracts-bedrock/src/L1/interfaces/IDelayedVetoable.sol b/packages/contracts-bedrock/src/L1/interfaces/IDelayedVetoable.sol deleted file mode 100644 index 53fd16812763..000000000000 --- a/packages/contracts-bedrock/src/L1/interfaces/IDelayedVetoable.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IDelayedVetoable { - error ForwardingEarly(); - error Unauthorized(address expected, address actual); - - event DelayActivated(uint256 delay); - event Forwarded(bytes32 indexed callHash, bytes data); - event Initiated(bytes32 indexed callHash, bytes data); - event Vetoed(bytes32 indexed callHash, bytes data); - - fallback() external; - - function delay() external returns (uint256 delay_); - function initiator() external returns (address initiator_); - function queuedAt(bytes32 _callHash) external returns (uint256 queuedAt_); - function target() external returns (address target_); - function version() external view returns (string memory); - function vetoer() external returns (address vetoer_); - - function __constructor__(address _vetoer, address _initiator, address _target, uint256 _operatingDelay) external; -} diff --git a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol b/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol deleted file mode 100644 index a0dd2d8c138e..000000000000 --- a/packages/contracts-bedrock/test/L1/DelayedVetoable.t.sol +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { Test } from "forge-std/Test.sol"; -import { IDelayedVetoable } from "src/L1/interfaces/IDelayedVetoable.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; - -contract DelayedVetoable_Init is Test { - error Unauthorized(address expected, address actual); - error ForwardingEarly(); - - event Initiated(bytes32 indexed callHash, bytes data); - event Forwarded(bytes32 indexed callHash, bytes data); - event Vetoed(bytes32 indexed callHash, bytes data); - - address target; - address initiator; - address vetoer; - uint256 operatingDelay = 14 days; - IDelayedVetoable delayedVetoable; - - function setUp() public { - initiator = makeAddr("initiator"); - vetoer = makeAddr("vetoer"); - target = makeAddr("target"); - vm.deal(initiator, 10000 ether); - vm.deal(vetoer, 10000 ether); - - delayedVetoable = IDelayedVetoable( - DeployUtils.create1({ - _name: "DelayedVetoable", - _args: DeployUtils.encodeConstructor( - abi.encodeCall(IDelayedVetoable.__constructor__, (vetoer, initiator, address(target), operatingDelay)) - ) - }) - ); - - // Most tests will use the operating delay, so we call as the initiator with null data - // to set the delay. For tests that need to use the initial zero delay, we'll modify the - // value in storage. - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(hex""); - assertTrue(success); - } - - /// @dev This function is used to prevent initiating the delay unintentionally. - /// It should only be used on tests prior to the delay being activated. - /// @param data The data to be used in the call. - function assumeNonzeroData(bytes memory data) internal pure { - vm.assume(data.length > 0); - } - - /// @dev This function is used to ensure that the data does not clash with the queuedAt function selector. - /// @param data The data to be used in the call. - function assumeNoClash(bytes calldata data) internal pure { - if (data.length >= 4) { - vm.assume(bytes4(data[0:4]) != bytes4(keccak256("queuedAt(bytes32)"))); - } - } -} - -contract DelayedVetoable_Getters_Test is DelayedVetoable_Init { - /// @dev The getters return the expected values when called by the zero address. - function test_getters_succeeds() external { - vm.startPrank(address(0)); - assertEq(delayedVetoable.initiator(), initiator); - assertEq(delayedVetoable.vetoer(), vetoer); - assertEq(delayedVetoable.target(), target); - assertEq(delayedVetoable.delay(), operatingDelay); - assertEq(delayedVetoable.queuedAt(keccak256(abi.encode(0))), 0); - } -} - -contract DelayedVetoable_Getters_TestFail is DelayedVetoable_Init { - /// @dev Check that getter calls from unauthorized entities will revert. - function test_getters_notZeroAddress_reverts() external { - vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); - delayedVetoable.initiator(); - vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); - delayedVetoable.vetoer(); - vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); - delayedVetoable.target(); - vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); - delayedVetoable.delay(); - vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); - delayedVetoable.queuedAt(keccak256(abi.encode(0))); - } -} - -contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init { - /// @dev A call can be initiated by the initiator. - function testFuzz_handleCall_initiation_succeeds(bytes calldata data) external { - assumeNoClash(data); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - emit Initiated(keccak256(data), data); - - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(data); - assertTrue(success); - } - - /// @dev The delay is inititially set to zero and the call is immediately forwarded. - function testFuzz_handleCall_initialForwardingImmediately_succeeds( - bytes calldata inData, - bytes calldata outData - ) - external - { - assumeNonzeroData(inData); - assumeNoClash(inData); - - // Reset the delay to zero - vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0))); - - vm.mockCall(target, inData, outData); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - vm.expectCall({ callee: target, data: inData }); - emit Forwarded(keccak256(inData), inData); - vm.prank(initiator); - (bool success, bytes memory returnData) = address(delayedVetoable).call(inData); - assertTrue(success); - assertEq(returnData, outData); - - // Check that the callHash is not stored for future forwarding - bytes32 callHash = keccak256(inData); - vm.prank(address(0)); - assertEq(delayedVetoable.queuedAt(callHash), 0); - } - - /// @dev Calls are not forwarded until the delay has passed. - function testFuzz_handleCall_forwardingWithDelay_succeeds(bytes calldata data) external { - assumeNonzeroData(data); - assumeNoClash(data); - - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(data); - - // Check that the call is in the _queuedAt mapping - bytes32 callHash = keccak256(data); - vm.prank(address(0)); - assertEq(delayedVetoable.queuedAt(callHash), block.timestamp); - - vm.warp(block.timestamp + operatingDelay); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - emit Forwarded(keccak256(data), data); - - vm.expectCall({ callee: target, data: data }); - (success,) = address(delayedVetoable).call(data); - assertTrue(success); - } -} - -contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { - /// @dev Only the initiator can initiate a call. - function test_handleCall_unauthorizedInitiation_reverts() external { - vm.expectRevert(abi.encodeWithSelector(IDelayedVetoable.Unauthorized.selector, initiator, address(this))); - (bool revertsAsExpected,) = address(delayedVetoable).call(hex"00001234"); - assertTrue(revertsAsExpected); - } - - /// @dev The call cannot be forwarded until the delay has passed. - function testFuzz_handleCall_forwardingTooSoon_reverts(bytes calldata data) external { - assumeNoClash(data); - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(data); - assertTrue(success); - - vm.expectRevert(IDelayedVetoable.ForwardingEarly.selector); - (bool revertsAsExpected,) = address(delayedVetoable).call(data); - assertTrue(revertsAsExpected); - } - - /// @dev The call cannot be forwarded a second time. - function testFuzz_handleCall_forwardingTwice_reverts(bytes calldata data) external { - assumeNoClash(data); - - // Initiate the call - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(data); - assertTrue(success); - - vm.warp(block.timestamp + operatingDelay); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - emit Forwarded(keccak256(data), data); - - // Forward the call - vm.expectCall({ callee: target, data: data }); - (success,) = address(delayedVetoable).call(data); - assertTrue(success); - - // Attempt to forward the same call again. - vm.expectRevert(abi.encodeWithSelector(IDelayedVetoable.Unauthorized.selector, initiator, address(this))); - (bool revertsAsExpected,) = address(delayedVetoable).call(data); - assertTrue(revertsAsExpected); - } - - /// @dev If the target reverts, it is bubbled up. - function testFuzz_handleCall_forwardingTargetReverts_reverts( - bytes calldata inData, - bytes calldata outData - ) - external - { - assumeNoClash(inData); - - // Initiate the call - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(inData); - assertTrue(success); - - vm.warp(block.timestamp + operatingDelay); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - emit Forwarded(keccak256(inData), inData); - - vm.mockCallRevert(target, inData, outData); - - // Forward the call - vm.expectRevert(outData); - (bool revertsAsExpected,) = address(delayedVetoable).call(inData); - assertTrue(revertsAsExpected); - } - - function testFuzz_handleCall_forwardingTargetRetValue_succeeds( - bytes calldata inData, - bytes calldata outData - ) - external - { - assumeNoClash(inData); - - // Initiate the call - vm.prank(initiator); - (bool success,) = address(delayedVetoable).call(inData); - assertTrue(success); - - vm.warp(block.timestamp + operatingDelay); - vm.expectEmit(true, false, false, true, address(delayedVetoable)); - emit Forwarded(keccak256(inData), inData); - - vm.mockCall(target, inData, outData); - - // Forward the call - (bool success2, bytes memory retData) = address(delayedVetoable).call(inData); - assertTrue(success2); - assertEq(keccak256(retData), keccak256(outData)); - } - - /// @dev A test documenting the single instance in which the contract is not 'transparent' to the initiator. - function testFuzz_handleCall_queuedAtClash_reverts() external { - // This will get us calldata with the same function selector as the queuedAt function, but - // with the incorrect input data length. - bytes memory inData = abi.encodePacked(keccak256("queuedAt(bytes32)")); - - // Reset the delay to zero - vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0))); - - vm.prank(initiator); - vm.expectRevert(bytes("")); - (bool revertsAsExpected,) = address(delayedVetoable).call(inData); - assertTrue(revertsAsExpected); - } -} diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 5150b4d725dd..3e5648fdfde0 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -106,14 +106,6 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "DataAvailabilityChallenge", _sel: IDataAvailabilityChallenge.resolve.selector }); _addSpec({ _name: "DataAvailabilityChallenge", _sel: IDataAvailabilityChallenge.unlockBond.selector }); - // DelayedVetoable - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("delay()") }); - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("initiator()") }); - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("queuedAt(bytes32)") }); - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("target()") }); - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("version()") }); - _addSpec({ _name: "DelayedVetoable", _sel: _getSel("vetoer()") }); - // L1CrossDomainMessenger _addSpec({ _name: "L1CrossDomainMessenger", _sel: _getSel("MESSAGE_VERSION()") }); _addSpec({ _name: "L1CrossDomainMessenger", _sel: _getSel("MIN_GAS_CALLDATA_OVERHEAD()") }); From c267d98b19cc06a6999e2e953d59cdf5a8326ac8 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Fri, 15 Nov 2024 18:31:01 +0100 Subject: [PATCH 187/451] improve tests SystemConfig (#12938) * improve tests * improve tests * fixes --- .../contracts-bedrock/test/L1/SystemConfig.t.sol | 13 +++++++++++++ .../test/L1/SystemConfigInterop.t.sol | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index a6311c02b1e4..fd5fd296f8fb 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -255,6 +255,19 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init { _initializeWithResourceConfig(config, "SystemConfig: gas limit too low"); } + /// @dev Tests that `setResourceConfig` reverts if the gas limit is too low. + function test_setResourceConfig_elasticityMultiplierIs0_reverts() external { + IResourceMetering.ResourceConfig memory config = IResourceMetering.ResourceConfig({ + maxResourceLimit: 20_000_000, + elasticityMultiplier: 0, + baseFeeMaxChangeDenominator: 8, + systemTxMaxGas: 1_000_000, + minimumBaseFee: 1 gwei, + maximumBaseFee: 2 gwei + }); + _initializeWithResourceConfig(config, "SystemConfig: elasticity multiplier cannot be 0"); + } + /// @dev Tests that `setResourceConfig` reverts if the elasticity multiplier /// and max resource limit are configured such that there is a loss of precision. function test_setResourceConfig_badPrecision_reverts() external { diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index 426dba30c72a..78337fc3b4d0 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -26,6 +26,19 @@ contract SystemConfigInterop_Test is CommonTest { super.setUp(); } + /// @dev Tests that when the decimals is not 18, initialization reverts. + function test_initialize_decimalsIsNot18_reverts(uint8 decimals) external { + vm.assume(decimals != 18); + address _token = address(L1Token); + + vm.mockCall(_token, abi.encodeCall(ERC20.name, ()), abi.encode("Token")); + vm.mockCall(_token, abi.encodeCall(ERC20.symbol, ()), abi.encode("TKN")); + vm.mockCall(_token, abi.encodeCall(ERC20.decimals, ()), abi.encode(decimals)); + + vm.expectRevert("SystemConfig: bad decimals of gas paying token"); + _cleanStorageAndInit(_token); + } + /// @dev Tests that the gas paying token can be set. function testFuzz_setGasPayingToken_succeeds( address _token, From 8f0a9b2d46f57b8ed4478a2ec39840ba61367684 Mon Sep 17 00:00:00 2001 From: blaine Date: Fri, 15 Nov 2024 16:08:36 -0500 Subject: [PATCH 188/451] opcm-redesign: opcm targets a single release (#12851) * fix: semver locking. * fix: semver locking. * feat: opcm impl contracts now type safe. * feat: fixing test. * fix: removing unused imports. * fix: address didn't need to be payable. * fix: moving all smart contract changes to first pr. * fix: pr comments addressed. * fix: removed InputContracts struct. * fix: ran pre-pr * fix: deploy implementations renaming version. * fix: adding solidity changes to this pr. * fix: adding v160 initializer back in. * fix: removed branching logic from opcm. * fix: removed SystemConfigV160. * opcm-redesign: op-deployer changes * fix: linting fix. * fix: semver lock --------- Co-authored-by: Matthew Slipper --- op-chain-ops/interopgen/configs.go | 2 +- op-chain-ops/interopgen/deploy.go | 5 +- op-chain-ops/interopgen/deployments.go | 3 +- op-chain-ops/interopgen/recipe.go | 2 +- op-deployer/pkg/deployer/bootstrap/opcm.go | 15 +- op-deployer/pkg/deployer/inspect/l1.go | 4 +- .../deployer/integration_test/apply_test.go | 26 +- op-deployer/pkg/deployer/opcm/contract.go | 51 -- .../pkg/deployer/opcm/implementations.go | 6 +- op-deployer/pkg/deployer/opcm/opchain.go | 6 +- op-deployer/pkg/deployer/pipeline/alt_da.go | 2 +- .../pkg/deployer/pipeline/implementations.go | 5 +- op-deployer/pkg/deployer/pipeline/init.go | 4 +- op-deployer/pkg/deployer/pipeline/opchain.go | 8 +- op-deployer/pkg/deployer/standard/standard.go | 9 +- op-deployer/pkg/deployer/state/state.go | 2 +- .../scripts/deploy/Deploy.s.sol | 10 +- .../deploy/DeployImplementations.s.sol | 439 +++++------------- .../scripts/deploy/DeployOPChain.s.sol | 35 +- .../deploy/ReadImplementationAddresses.s.sol | 26 +- .../snapshots/abi/OPContractsManager.json | 271 ++++++----- .../abi/OPContractsManagerInterop.json | 271 ++++++----- .../snapshots/semver-lock.json | 4 +- .../storageLayout/OPContractsManager.json | 37 +- .../OPContractsManagerInterop.json | 37 +- .../src/L1/OPContractsManager.sol | 288 +++++------- .../src/L1/OPContractsManagerInterop.sol | 15 +- .../src/L1/interfaces/ISystemConfigV160.sol | 85 ---- .../test/L1/OPContractsManager.t.sol | 16 +- .../test/opcm/DeployImplementations.t.sol | 114 +---- .../test/opcm/DeployOPChain.t.sol | 19 +- .../test/universal/Specs.t.sol | 34 +- 32 files changed, 666 insertions(+), 1185 deletions(-) delete mode 100644 packages/contracts-bedrock/src/L1/interfaces/ISystemConfigV160.sol diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index 942588a9929c..948d9daa3053 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -35,7 +35,7 @@ type SuperFaultProofConfig struct { } type OPCMImplementationsConfig struct { - Release string + L1ContractsRelease string FaultProof SuperFaultProofConfig diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 476406821ea5..e915b724e10b 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -170,10 +170,9 @@ func DeploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup ProofMaturityDelaySeconds: superCfg.Implementations.FaultProof.ProofMaturityDelaySeconds, DisputeGameFinalityDelaySeconds: superCfg.Implementations.FaultProof.DisputeGameFinalityDelaySeconds, MipsVersion: superCfg.Implementations.FaultProof.MipsVersion, - Release: superCfg.Implementations.Release, + L1ContractsRelease: superCfg.Implementations.L1ContractsRelease, SuperchainConfigProxy: superDeployment.SuperchainConfigProxy, ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy, - OpcmProxyOwner: superDeployment.SuperchainProxyAdmin, UseInterop: superCfg.Implementations.UseInterop, StandardVersionsToml: standard.VersionsMainnetData, }) @@ -210,7 +209,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), - OpcmProxy: superDeployment.OpcmProxy, + Opcm: superDeployment.Opcm, SaltMixer: cfg.SaltMixer, GasLimit: cfg.GasLimit, DisputeGameType: cfg.DisputeGameType, diff --git a/op-chain-ops/interopgen/deployments.go b/op-chain-ops/interopgen/deployments.go index ba18fbfdf9bd..f98a0554d870 100644 --- a/op-chain-ops/interopgen/deployments.go +++ b/op-chain-ops/interopgen/deployments.go @@ -9,8 +9,7 @@ type L1Deployment struct { } type Implementations struct { - OpcmProxy common.Address `json:"OPCMProxy"` - OpcmImpl common.Address `json:"OPCMImpl"` + Opcm common.Address `json:"OPCM"` DelayedWETHImpl common.Address `json:"DelayedWETHImpl"` OptimismPortalImpl common.Address `json:"OptimismPortalImpl"` PreimageOracleSingleton common.Address `json:"PreimageOracleSingleton"` diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index 8983ee72da8e..e70c69e9f481 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -69,7 +69,7 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) ProtocolVersionsOwner: superchainProtocolVersionsOwner, Deployer: superchainDeployer, Implementations: OPCMImplementationsConfig{ - Release: "dev", + L1ContractsRelease: "dev", FaultProof: SuperFaultProofConfig{ WithdrawalDelaySeconds: big.NewInt(604800), MinProposalSizeBytes: big.NewInt(10000), diff --git a/op-deployer/pkg/deployer/bootstrap/opcm.go b/op-deployer/pkg/deployer/bootstrap/opcm.go index 2f5976f304ad..89a8c3df5123 100644 --- a/op-deployer/pkg/deployer/bootstrap/opcm.go +++ b/op-deployer/pkg/deployer/bootstrap/opcm.go @@ -164,10 +164,6 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { if err != nil { return fmt.Errorf("error getting standard versions TOML: %w", err) } - opcmProxyOwnerAddr, err := standard.ManagerOwnerAddrFor(chainIDU64) - if err != nil { - return fmt.Errorf("error getting superchain proxy admin: %w", err) - } signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) @@ -199,14 +195,14 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { } host.SetNonce(chainDeployer, nonce) - var release string + var l1ContractsRelease string if cfg.ArtifactsLocator.IsTag() { - release = cfg.ArtifactsLocator.Tag + l1ContractsRelease = cfg.ArtifactsLocator.Tag } else { - release = "dev" + l1ContractsRelease = "dev" } - lgr.Info("deploying OPCM", "release", release) + lgr.Info("deploying OPCM", "l1ContractsRelease", l1ContractsRelease) // We need to etch the Superchain addresses so that they have nonzero code // and the checks in the OPCM constructor pass. @@ -238,10 +234,9 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ProofMaturityDelaySeconds: new(big.Int).SetUint64(cfg.ProofMaturityDelaySeconds), DisputeGameFinalityDelaySeconds: new(big.Int).SetUint64(cfg.DisputeGameFinalityDelaySeconds), MipsVersion: new(big.Int).SetUint64(cfg.MIPSVersion), - Release: release, + L1ContractsRelease: l1ContractsRelease, SuperchainConfigProxy: superchainConfigAddr, ProtocolVersionsProxy: protocolVersionsAddr, - OpcmProxyOwner: opcmProxyOwnerAddr, StandardVersionsToml: standardVersionsTOML, UseInterop: false, }, diff --git a/op-deployer/pkg/deployer/inspect/l1.go b/op-deployer/pkg/deployer/inspect/l1.go index d0a4f88172d1..4883e83486c3 100644 --- a/op-deployer/pkg/deployer/inspect/l1.go +++ b/op-deployer/pkg/deployer/inspect/l1.go @@ -45,7 +45,7 @@ type OpChainDeployment struct { } type ImplementationsDeployment struct { - OpcmProxyAddress common.Address `json:"opcmProxyAddress"` + OpcmAddress common.Address `json:"opcmAddress"` DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"` OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"` PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"` @@ -113,7 +113,7 @@ func L1(globalState *state.State, chainID common.Hash) (*L1Contracts, error) { // DelayedWETHPermissionlessGameProxyAddress: chainState.DelayedWETHPermissionlessGameProxyAddress, }, ImplementationsDeployment: ImplementationsDeployment{ - OpcmProxyAddress: globalState.ImplementationsDeployment.OpcmProxyAddress, + OpcmAddress: globalState.ImplementationsDeployment.OpcmAddress, DelayedWETHImplAddress: globalState.ImplementationsDeployment.DelayedWETHImplAddress, OptimismPortalImplAddress: globalState.ImplementationsDeployment.OptimismPortalImplAddress, PreimageOracleSingletonAddress: globalState.ImplementationsDeployment.PreimageOracleSingletonAddress, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 215687c5cfc0..bcbc14eb7f19 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -207,6 +207,30 @@ func TestApplyExistingOPCM(t *testing.T) { )) validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) + + releases := standard.L1VersionsSepolia.Releases["op-contracts/v1.6.0"] + + implTests := []struct { + name string + expAddr common.Address + actAddr common.Address + }{ + {"OptimismPortal", releases.OptimismPortal.ImplementationAddress, st.ImplementationsDeployment.OptimismPortalImplAddress}, + {"SystemConfig,", releases.SystemConfig.ImplementationAddress, st.ImplementationsDeployment.SystemConfigImplAddress}, + {"L1CrossDomainMessenger", releases.L1CrossDomainMessenger.ImplementationAddress, st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress}, + {"L1ERC721Bridge", releases.L1ERC721Bridge.ImplementationAddress, st.ImplementationsDeployment.L1ERC721BridgeImplAddress}, + {"L1StandardBridge", releases.L1StandardBridge.ImplementationAddress, st.ImplementationsDeployment.L1StandardBridgeImplAddress}, + {"OptimismMintableERC20Factory", releases.OptimismMintableERC20Factory.ImplementationAddress, st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress}, + {"DisputeGameFactory", releases.DisputeGameFactory.ImplementationAddress, st.ImplementationsDeployment.DisputeGameFactoryImplAddress}, + {"MIPS", releases.MIPS.Address, st.ImplementationsDeployment.MipsSingletonAddress}, + {"PreimageOracle", releases.PreimageOracle.Address, st.ImplementationsDeployment.PreimageOracleSingletonAddress}, + {"DelayedWETH", releases.DelayedWETH.ImplementationAddress, st.ImplementationsDeployment.DelayedWETHImplAddress}, + } + for _, tt := range implTests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expAddr, tt.actAddr) + }) + } } func TestL2BlockTimeOverride(t *testing.T) { @@ -571,7 +595,7 @@ func validateSuperchainDeployment(t *testing.T, st *state.State, cg codeGetter) {"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress}, {"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress}, {"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress}, - {"OpcmProxy", st.ImplementationsDeployment.OpcmProxyAddress}, + {"Opcm", st.ImplementationsDeployment.OpcmAddress}, {"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, {"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress}, } diff --git a/op-deployer/pkg/deployer/opcm/contract.go b/op-deployer/pkg/deployer/opcm/contract.go index b90db5814192..8d02f77e8e0a 100644 --- a/op-deployer/pkg/deployer/opcm/contract.go +++ b/op-deployer/pkg/deployer/opcm/contract.go @@ -48,57 +48,6 @@ func (c *Contract) GenericAddressGetter(ctx context.Context, functionName string return c.callContractMethod(ctx, functionName, abi.Arguments{}) } -// GetImplementation retrieves the Implementation struct for a given release and contract name. -func (c *Contract) GetOPCMImplementationAddress(ctx context.Context, release, contractName string) (common.Address, error) { - methodName := "implementations" - method := abi.NewMethod( - methodName, - methodName, - abi.Function, - "view", - true, - false, - abi.Arguments{ - {Name: "release", Type: mustType("string")}, - {Name: "contractName", Type: mustType("string")}, - }, - abi.Arguments{ - {Name: "logic", Type: mustType("address")}, - {Name: "initializer", Type: mustType("bytes4")}, - }, - ) - - calldata, err := method.Inputs.Pack(release, contractName) - if err != nil { - return common.Address{}, fmt.Errorf("failed to pack inputs: %w", err) - } - - msg := ethereum.CallMsg{ - To: &c.addr, - Data: append(bytes.Clone(method.ID), calldata...), - } - - result, err := c.client.CallContract(ctx, msg, nil) - if err != nil { - return common.Address{}, fmt.Errorf("failed to call contract: %w", err) - } - - out, err := method.Outputs.Unpack(result) - if err != nil { - return common.Address{}, fmt.Errorf("failed to unpack result: %w", err) - } - if len(out) != 2 { - return common.Address{}, fmt.Errorf("unexpected output length: %d", len(out)) - } - - logic, ok := out[0].(common.Address) - if !ok { - return common.Address{}, fmt.Errorf("unexpected type for logic: %T", out[0]) - } - - return logic, nil -} - func (c *Contract) callContractMethod(ctx context.Context, methodName string, inputs abi.Arguments, args ...interface{}) (common.Address, error) { method := abi.NewMethod( methodName, diff --git a/op-deployer/pkg/deployer/opcm/implementations.go b/op-deployer/pkg/deployer/opcm/implementations.go index 8dd072eef246..413452b1d348 100644 --- a/op-deployer/pkg/deployer/opcm/implementations.go +++ b/op-deployer/pkg/deployer/opcm/implementations.go @@ -18,12 +18,11 @@ type DeployImplementationsInput struct { DisputeGameFinalityDelaySeconds *big.Int MipsVersion *big.Int // Release version to set OPCM implementations for, of the format `op-contracts/vX.Y.Z`. - Release string + L1ContractsRelease string SuperchainConfigProxy common.Address ProtocolVersionsProxy common.Address UseInterop bool // if true, deploy Interop implementations - OpcmProxyOwner common.Address StandardVersionsToml string // contents of 'standard-versions-mainnet.toml' or 'standard-versions-sepolia.toml' file } @@ -32,8 +31,7 @@ func (input *DeployImplementationsInput) InputSet() bool { } type DeployImplementationsOutput struct { - OpcmProxy common.Address - OpcmImpl common.Address + Opcm common.Address DelayedWETHImpl common.Address OptimismPortalImpl common.Address PreimageOracleSingleton common.Address diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 1e1b468a417f..8c7e60fec4d2 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -26,7 +26,7 @@ type DeployOPChainInputV160 struct { BasefeeScalar uint32 BlobBaseFeeScalar uint32 L2ChainId *big.Int - OpcmProxy common.Address + Opcm common.Address SaltMixer string GasLimit uint64 @@ -122,8 +122,8 @@ func deployOPChain[T any](host *script.Host, input T) (DeployOPChainOutput, erro type ReadImplementationAddressesInput struct { DeployOPChainOutput - OpcmProxy common.Address - Release string + Opcm common.Address + Release string } type ReadImplementationAddressesOutput struct { diff --git a/op-deployer/pkg/deployer/pipeline/alt_da.go b/op-deployer/pkg/deployer/pipeline/alt_da.go index 62796832c93c..b36412620945 100644 --- a/op-deployer/pkg/deployer/pipeline/alt_da.go +++ b/op-deployer/pkg/deployer/pipeline/alt_da.go @@ -31,7 +31,7 @@ func DeployAltDA(env *Env, intent *state.Intent, st *state.State, chainID common lgr.Info("deploying alt-da contracts") dao, err = opcm.DeployAltDA(env.L1ScriptHost, opcm.DeployAltDAInput{ Salt: st.Create2Salt, - ProxyAdmin: st.ImplementationsDeployment.OpcmProxyAddress, + ProxyAdmin: chainState.ProxyAdminAddress, ChallengeContractOwner: chainIntent.Roles.L1ProxyAdminOwner, ChallengeWindow: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DAChallengeWindow), ResolveWindow: new(big.Int).SetUint64(chainIntent.DangerousAltDAConfig.DAResolveWindow), diff --git a/op-deployer/pkg/deployer/pipeline/implementations.go b/op-deployer/pkg/deployer/pipeline/implementations.go index 47ea91fbe963..9e65fd4f54a3 100644 --- a/op-deployer/pkg/deployer/pipeline/implementations.go +++ b/op-deployer/pkg/deployer/pipeline/implementations.go @@ -68,10 +68,9 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro ProofMaturityDelaySeconds: new(big.Int).SetUint64(proofParams.ProofMaturityDelaySeconds), DisputeGameFinalityDelaySeconds: new(big.Int).SetUint64(proofParams.DisputeGameFinalityDelaySeconds), MipsVersion: new(big.Int).SetUint64(proofParams.MIPSVersion), - Release: contractsRelease, + L1ContractsRelease: contractsRelease, SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress, ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress, - OpcmProxyOwner: st.SuperchainDeployment.ProxyAdminAddress, StandardVersionsToml: standardVersionsTOML, UseInterop: intent.UseInterop, }, @@ -81,7 +80,7 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro } st.ImplementationsDeployment = &state.ImplementationsDeployment{ - OpcmProxyAddress: dio.OpcmProxy, + OpcmAddress: dio.Opcm, DelayedWETHImplAddress: dio.DelayedWETHImpl, OptimismPortalImplAddress: dio.OptimismPortalImpl, PreimageOracleSingletonAddress: dio.PreimageOracleSingleton, diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index f8b12e36a431..b5c37c246f29 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -45,12 +45,12 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr), } - opcmProxy, err := standard.ManagerImplementationAddrFor(intent.L1ChainID) + opcmAddress, err := standard.ManagerImplementationAddrFor(intent.L1ChainID) if err != nil { return fmt.Errorf("error getting OPCM proxy address: %w", err) } st.ImplementationsDeployment = &state.ImplementationsDeployment{ - OpcmProxyAddress: opcmProxy, + OpcmAddress: opcmAddress, } } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index a49848883cea..90cc665e077d 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -34,7 +34,7 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) } - opcmAddr = input.OpcmProxy + opcmAddr = input.Opcm return opcm.DeployOPChainV160(env.L1ScriptHost, input) } default: @@ -44,7 +44,7 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) } - opcmAddr = input.OpcmProxy + opcmAddr = input.Opcm return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input) } } @@ -67,7 +67,7 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm readInput := opcm.ReadImplementationAddressesInput{ DeployOPChainOutput: dco, - OpcmProxy: opcmAddr, + Opcm: opcmAddr, Release: release, } impls, err := opcm.ReadImplementationAddresses(env.L1ScriptHost, readInput) @@ -126,7 +126,7 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co BasefeeScalar: standard.BasefeeScalar, BlobBaseFeeScalar: standard.BlobBaseFeeScalar, L2ChainId: chainID.Big(), - OpcmProxy: st.ImplementationsDeployment.OpcmProxyAddress, + Opcm: st.ImplementationsDeployment.OpcmAddress, SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization GasLimit: standard.GasLimit, DisputeGameType: proofParams.DisputeGameType, diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 2fb6de8c5bf7..8c4ade1dcfb3 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -134,10 +134,11 @@ func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) { switch chainID { case 1: // Generated using the bootstrap command on 10/18/2024. - return common.HexToAddress("0x18cec91779995ad14c880e4095456b9147160790"), nil + // TODO: @blmalone this needs re-bootstrapped because it's still proxied + return common.HexToAddress(""), nil case 11155111: - // Generated using the bootstrap command on 10/18/2024. - return common.HexToAddress("0xf564eea7960ea244bfebcbbb17858748606147bf"), nil + // Generated using the bootstrap command on 11/15/2024. + return common.HexToAddress("0xde9eacb994a6eb12997445f8a63a22772c5c4313"), nil default: return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) } @@ -172,7 +173,7 @@ func SystemOwnerAddrFor(chainID uint64) (common.Address, error) { func ArtifactsURLForTag(tag string) (*url.URL, error) { switch tag { case "op-contracts/v1.6.0": - return url.Parse(standardArtifactsURL("3a27c6dc0cb61b36feaac26def98c64b4a48ec8f5c5ba6965e8ae3157606043c")) + return url.Parse(standardArtifactsURL("e1f0c4020618c4a98972e7124c39686cab2e31d5d7846f9ce5e0d5eed0f5ff32")) case "op-contracts/v1.7.0-beta.1+l2-contracts": return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597")) default: diff --git a/op-deployer/pkg/deployer/state/state.go b/op-deployer/pkg/deployer/state/state.go index e3974fa2a78c..e17ed6184c5d 100644 --- a/op-deployer/pkg/deployer/state/state.go +++ b/op-deployer/pkg/deployer/state/state.go @@ -64,7 +64,7 @@ type SuperchainDeployment struct { } type ImplementationsDeployment struct { - OpcmProxyAddress common.Address `json:"opcmProxyAddress"` + OpcmAddress common.Address `json:"opcmAddress"` DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"` OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"` PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"` diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 54cc1a23e172..0253e20a1956 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -162,7 +162,7 @@ contract Deploy is Deployer { L1ERC721Bridge: getAddress("L1ERC721BridgeProxy"), ProtocolVersions: getAddress("ProtocolVersionsProxy"), SuperchainConfig: getAddress("SuperchainConfigProxy"), - OPContractsManager: getAddress("OPContractsManagerProxy") + OPContractsManager: getAddress("OPContractsManager") }); } @@ -378,13 +378,12 @@ contract Deploy is Deployer { dii.set(dii.disputeGameFinalityDelaySeconds.selector, cfg.disputeGameFinalityDelaySeconds()); dii.set(dii.mipsVersion.selector, Config.useMultithreadedCannon() ? 2 : 1); string memory release = "dev"; - dii.set(dii.release.selector, release); + dii.set(dii.l1ContractsRelease.selector, release); dii.set( dii.standardVersionsToml.selector, string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml") ); dii.set(dii.superchainConfigProxy.selector, mustGetAddress("SuperchainConfigProxy")); dii.set(dii.protocolVersionsProxy.selector, mustGetAddress("ProtocolVersionsProxy")); - dii.set(dii.opcmProxyOwner.selector, cfg.finalSystemOwner()); if (_isInterop) { di = DeployImplementations(new DeployImplementationsInterop()); @@ -409,8 +408,7 @@ contract Deploy is Deployer { save("DelayedWETH", address(dio.delayedWETHImpl())); save("PreimageOracle", address(dio.preimageOracleSingleton())); save("Mips", address(dio.mipsSingleton())); - save("OPContractsManagerProxy", address(dio.opcmProxy())); - save("OPContractsManager", address(dio.opcmImpl())); + save("OPContractsManager", address(dio.opcm())); Types.ContractSet memory contracts = _impls(); ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _vm: vm, _isProxy: false }); @@ -446,7 +444,7 @@ contract Deploy is Deployer { // Ensure that the requisite contracts are deployed address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy"); - OPContractsManager opcm = OPContractsManager(mustGetAddress("OPContractsManagerProxy")); + OPContractsManager opcm = OPContractsManager(mustGetAddress("OPContractsManager")); OPContractsManager.DeployInput memory deployInput = getDeployInput(); OPContractsManager.DeployOutput memory deployOutput = opcm.deploy(deployInput); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index c9048a07dfa3..9abff9149181 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -8,16 +8,11 @@ import { LibString } from "@solady/utils/LibString.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; -import { ISystemConfigV160 } from "src/L1/interfaces/ISystemConfigV160.sol"; -import { IL1CrossDomainMessengerV160 } from "src/L1/interfaces/IL1CrossDomainMessengerV160.sol"; -import { IL1StandardBridgeV160 } from "src/L1/interfaces/IL1StandardBridgeV160.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Bytes } from "src/libraries/Bytes.sol"; -import { IProxy } from "src/universal/interfaces/IProxy.sol"; - import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; import { IMIPS } from "src/cannon/interfaces/IMIPS.sol"; @@ -51,8 +46,9 @@ contract DeployImplementationsInput is BaseDeployIO { uint256 internal _disputeGameFinalityDelaySeconds; uint256 internal _mipsVersion; - // The release version to set OPCM implementations for, of the format `op-contracts/vX.Y.Z`. - string internal _release; + // This is used in opcm to signal which version of the L1 smart contracts is deployed. + // It takes the format of `op-contracts/v*.*.*`. + string internal _l1ContractsRelease; // Outputs from DeploySuperchain.s.sol. ISuperchainConfig internal _superchainConfigProxy; @@ -60,8 +56,6 @@ contract DeployImplementationsInput is BaseDeployIO { string internal _standardVersionsToml; - address internal _opcmProxyOwner; - function set(bytes4 _sel, uint256 _value) public { require(_value != 0, "DeployImplementationsInput: cannot set zero value"); @@ -85,7 +79,7 @@ contract DeployImplementationsInput is BaseDeployIO { function set(bytes4 _sel, string memory _value) public { require(!LibString.eq(_value, ""), "DeployImplementationsInput: cannot set empty string"); - if (_sel == this.release.selector) _release = _value; + if (_sel == this.l1ContractsRelease.selector) _l1ContractsRelease = _value; else if (_sel == this.standardVersionsToml.selector) _standardVersionsToml = _value; else revert("DeployImplementationsInput: unknown selector"); } @@ -94,7 +88,6 @@ contract DeployImplementationsInput is BaseDeployIO { require(_addr != address(0), "DeployImplementationsInput: cannot set zero address"); if (_sel == this.superchainConfigProxy.selector) _superchainConfigProxy = ISuperchainConfig(_addr); else if (_sel == this.protocolVersionsProxy.selector) _protocolVersionsProxy = IProtocolVersions(_addr); - else if (_sel == this.opcmProxyOwner.selector) _opcmProxyOwner = _addr; else revert("DeployImplementationsInput: unknown selector"); } @@ -141,9 +134,9 @@ contract DeployImplementationsInput is BaseDeployIO { return _mipsVersion; } - function release() public view returns (string memory) { - require(!LibString.eq(_release, ""), "DeployImplementationsInput: not set"); - return _release; + function l1ContractsRelease() public view returns (string memory) { + require(!LibString.eq(_l1ContractsRelease, ""), "DeployImplementationsInput: not set"); + return _l1ContractsRelease; } function standardVersionsToml() public view returns (string memory) { @@ -160,16 +153,10 @@ contract DeployImplementationsInput is BaseDeployIO { require(address(_protocolVersionsProxy) != address(0), "DeployImplementationsInput: not set"); return _protocolVersionsProxy; } - - function opcmProxyOwner() public view returns (address) { - require(address(_opcmProxyOwner) != address(0), "DeployImplementationsInput: not set"); - return _opcmProxyOwner; - } } contract DeployImplementationsOutput is BaseDeployIO { - OPContractsManager internal _opcmProxy; - OPContractsManager internal _opcmImpl; + OPContractsManager internal _opcm; IDelayedWETH internal _delayedWETHImpl; IOptimismPortal2 internal _optimismPortalImpl; IPreimageOracle internal _preimageOracleSingleton; @@ -185,8 +172,7 @@ contract DeployImplementationsOutput is BaseDeployIO { require(_addr != address(0), "DeployImplementationsOutput: cannot set zero address"); // forgefmt: disable-start - if (_sel == this.opcmProxy.selector) _opcmProxy = OPContractsManager(payable(_addr)); - else if (_sel == this.opcmImpl.selector) _opcmImpl = OPContractsManager(payable(_addr)); + if (_sel == this.opcm.selector) _opcm = OPContractsManager(_addr); else if (_sel == this.optimismPortalImpl.selector) _optimismPortalImpl = IOptimismPortal2(payable(_addr)); else if (_sel == this.delayedWETHImpl.selector) _delayedWETHImpl = IDelayedWETH(payable(_addr)); else if (_sel == this.preimageOracleSingleton.selector) _preimageOracleSingleton = IPreimageOracle(_addr); @@ -201,12 +187,11 @@ contract DeployImplementationsOutput is BaseDeployIO { // forgefmt: disable-end } - function checkOutput(DeployImplementationsInput _dii) public { + function checkOutput(DeployImplementationsInput _dii) public view { // With 12 addresses, we'd get a stack too deep error if we tried to do this inline as a // single call to `Solarray.addresses`. So we split it into two calls. address[] memory addrs1 = Solarray.addresses( - address(this.opcmProxy()), - address(this.opcmImpl()), + address(this.opcm()), address(this.optimismPortalImpl()), address(this.delayedWETHImpl()), address(this.preimageOracleSingleton()), @@ -227,15 +212,9 @@ contract DeployImplementationsOutput is BaseDeployIO { assertValidDeploy(_dii); } - function opcmProxy() public returns (OPContractsManager) { - DeployUtils.assertValidContractAddress(address(_opcmProxy)); - DeployUtils.assertERC1967ImplementationSet(address(_opcmProxy)); - return _opcmProxy; - } - - function opcmImpl() public view returns (OPContractsManager) { - DeployUtils.assertValidContractAddress(address(_opcmImpl)); - return _opcmImpl; + function opcm() public view returns (OPContractsManager) { + DeployUtils.assertValidContractAddress(address(_opcm)); + return _opcm; } function optimismPortalImpl() public view returns (IOptimismPortal2) { @@ -289,40 +268,22 @@ contract DeployImplementationsOutput is BaseDeployIO { } // -------- Deployment Assertions -------- - function assertValidDeploy(DeployImplementationsInput _dii) public { + function assertValidDeploy(DeployImplementationsInput _dii) public view { assertValidDelayedWETHImpl(_dii); assertValidDisputeGameFactoryImpl(_dii); assertValidL1CrossDomainMessengerImpl(_dii); assertValidL1ERC721BridgeImpl(_dii); assertValidL1StandardBridgeImpl(_dii); assertValidMipsSingleton(_dii); - assertValidOpcmProxy(_dii); - assertValidOpcmImpl(_dii); + assertValidOpcm(_dii); assertValidOptimismMintableERC20FactoryImpl(_dii); assertValidOptimismPortalImpl(_dii); assertValidPreimageOracleSingleton(_dii); assertValidSystemConfigImpl(_dii); } - function assertValidOpcmProxy(DeployImplementationsInput _dii) internal { - // First we check the proxy as itself. - IProxy proxy = IProxy(payable(address(opcmProxy()))); - vm.prank(address(0)); - address admin = proxy.admin(); - require(admin == address(_dii.opcmProxyOwner()), "OPCMP-10"); - - // Then we check the proxy as OPCM. - DeployUtils.assertInitialized({ _contractAddress: address(opcmProxy()), _slot: 0, _offset: 0 }); - require(address(opcmProxy().superchainConfig()) == address(_dii.superchainConfigProxy()), "OPCMP-20"); - require(address(opcmProxy().protocolVersions()) == address(_dii.protocolVersionsProxy()), "OPCMP-30"); - require(LibString.eq(opcmProxy().latestRelease(), _dii.release()), "OPCMP-50"); // Initial release is latest. - } - - function assertValidOpcmImpl(DeployImplementationsInput _dii) internal { - IProxy proxy = IProxy(payable(address(opcmProxy()))); - vm.prank(address(0)); - OPContractsManager impl = OPContractsManager(proxy.implementation()); - DeployUtils.assertInitialized({ _contractAddress: address(impl), _slot: 0, _offset: 0 }); + function assertValidOpcm(DeployImplementationsInput _dii) internal view { + OPContractsManager impl = OPContractsManager(address(opcm())); require(address(impl.superchainConfig()) == address(_dii.superchainConfigProxy()), "OPCMI-10"); require(address(impl.protocolVersions()) == address(_dii.protocolVersionsProxy()), "OPCMI-20"); } @@ -361,7 +322,6 @@ contract DeployImplementationsOutput is BaseDeployIO { function assertValidMipsSingleton(DeployImplementationsInput) internal view { IMIPS mips = mipsSingleton(); - require(address(mips.oracle()) == address(preimageOracleSingleton()), "MIPS-10"); } @@ -480,102 +440,38 @@ contract DeployImplementations is Script { // --- OP Contracts Manager --- - function opcmSystemConfigSetter( - DeployImplementationsInput _dii, - DeployImplementationsOutput _dio - ) - internal - view - virtual - returns (OPContractsManager.ImplementationSetter memory) - { - // When configuring OPCM during Solidity tests, we are using the latest SystemConfig.sol - // version in this repo, which contains Custom Gas Token (CGT) features. This CGT version - // has a different `initialize` signature than the SystemConfig version that was released - // as part of `op-contracts/v1.6.0`, which is no longer in the repo. When running this - // script's bytecode for a production deploy of OPCM at `op-contracts/v1.6.0`, we need to - // use the ISystemConfigV160 interface instead of ISystemConfig. Therefore the selector used - // is a function of the `release` passed in by the caller. - bytes4 selector = LibString.eq(_dii.release(), "op-contracts/v1.6.0") - ? ISystemConfigV160.initialize.selector - : ISystemConfig.initialize.selector; - return OPContractsManager.ImplementationSetter({ - name: "SystemConfig", - info: OPContractsManager.Implementation(address(_dio.systemConfigImpl()), selector) - }); - } - - function l1CrossDomainMessengerConfigSetter( - DeployImplementationsInput _dii, - DeployImplementationsOutput _dio - ) - internal - view - virtual - returns (OPContractsManager.ImplementationSetter memory) - { - bytes4 selector = LibString.eq(_dii.release(), "op-contracts/v1.6.0") - ? IL1CrossDomainMessengerV160.initialize.selector - : IL1CrossDomainMessenger.initialize.selector; - return OPContractsManager.ImplementationSetter({ - name: "L1CrossDomainMessenger", - info: OPContractsManager.Implementation(address(_dio.l1CrossDomainMessengerImpl()), selector) - }); - } - - function l1StandardBridgeConfigSetter( - DeployImplementationsInput _dii, - DeployImplementationsOutput _dio - ) - internal - view - virtual - returns (OPContractsManager.ImplementationSetter memory) - { - bytes4 selector = LibString.eq(_dii.release(), "op-contracts/v1.6.0") - ? IL1StandardBridgeV160.initialize.selector - : IL1StandardBridge.initialize.selector; - return OPContractsManager.ImplementationSetter({ - name: "L1StandardBridge", - info: OPContractsManager.Implementation(address(_dio.l1StandardBridgeImpl()), selector) - }); - } - - // Deploy and initialize a proxied OPContractsManager. function createOPCMContract( DeployImplementationsInput _dii, DeployImplementationsOutput _dio, OPContractsManager.Blueprints memory _blueprints, - string memory _release, - OPContractsManager.ImplementationSetter[] memory _setters + string memory _l1ContractsRelease ) internal virtual - returns (OPContractsManager opcmProxy_) + returns (OPContractsManager opcm_) { - address opcmProxyOwner = _dii.opcmProxyOwner(); + ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); + IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); + + OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({ + l1ERC721BridgeImpl: address(_dio.l1ERC721BridgeImpl()), + optimismPortalImpl: address(_dio.optimismPortalImpl()), + systemConfigImpl: address(_dio.systemConfigImpl()), + optimismMintableERC20FactoryImpl: address(_dio.optimismMintableERC20FactoryImpl()), + l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()), + l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()), + disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()), + delayedWETHImpl: address(_dio.delayedWETHImpl()), + mipsImpl: address(_dio.mipsSingleton()) + }); vm.broadcast(msg.sender); - IProxy proxy = IProxy( - DeployUtils.create1({ - _name: "Proxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) - }) + opcm_ = new OPContractsManager( + superchainConfigProxy, protocolVersionsProxy, _l1ContractsRelease, _blueprints, implementations ); - deployOPContractsManagerImpl(_dii, _dio); - OPContractsManager opcmImpl = _dio.opcmImpl(); - - OPContractsManager.InitializerInputs memory initializerInputs = - OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); - - vm.startBroadcast(msg.sender); - proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); - - proxy.changeAdmin(address(opcmProxyOwner)); // transfer ownership of Proxy contract to the ProxyAdmin contract - vm.stopBroadcast(); - - opcmProxy_ = OPContractsManager(address(proxy)); + vm.label(address(opcm_), "OPContractsManager"); + _dio.set(_dio.opcm.selector, address(opcm_)); } function deployOPContractsManager( @@ -585,72 +481,42 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); - - // First we deploy the blueprints for the singletons deployed by OPCM. - // forgefmt: disable-start - bytes32 salt = _dii.salt(); - OPContractsManager.Blueprints memory blueprints; - - vm.startBroadcast(msg.sender); - blueprints.addressManager = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AddressManager")), salt); - blueprints.proxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("Proxy")), salt); - blueprints.proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt); - blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt); - blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt); - blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt); - (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt); - vm.stopBroadcast(); - // forgefmt: disable-end - - OPContractsManager.ImplementationSetter[] memory setters = new OPContractsManager.ImplementationSetter[](9); - setters[0] = OPContractsManager.ImplementationSetter({ - name: "L1ERC721Bridge", - info: OPContractsManager.Implementation(address(_dio.l1ERC721BridgeImpl()), IL1ERC721Bridge.initialize.selector) - }); - setters[1] = OPContractsManager.ImplementationSetter({ - name: "OptimismPortal", - info: OPContractsManager.Implementation( - address(_dio.optimismPortalImpl()), IOptimismPortal2.initialize.selector - ) - }); - setters[2] = opcmSystemConfigSetter(_dii, _dio); - setters[3] = OPContractsManager.ImplementationSetter({ - name: "OptimismMintableERC20Factory", - info: OPContractsManager.Implementation( - address(_dio.optimismMintableERC20FactoryImpl()), IOptimismMintableERC20Factory.initialize.selector - ) - }); - setters[4] = l1CrossDomainMessengerConfigSetter(_dii, _dio); - setters[5] = l1StandardBridgeConfigSetter(_dii, _dio); - setters[6] = OPContractsManager.ImplementationSetter({ - name: "DisputeGameFactory", - info: OPContractsManager.Implementation( - address(_dio.disputeGameFactoryImpl()), IDisputeGameFactory.initialize.selector - ) - }); - setters[7] = OPContractsManager.ImplementationSetter({ - name: "DelayedWETH", - info: OPContractsManager.Implementation(address(_dio.delayedWETHImpl()), IDelayedWETH.initialize.selector) - }); - setters[8] = OPContractsManager.ImplementationSetter({ - name: "MIPS", - // MIPS is a singleton for all chains, so it doesn't need to be initialized, so the - // selector is just `bytes4(0)`. - info: OPContractsManager.Implementation(address(_dio.mipsSingleton()), bytes4(0)) - }); + string memory l1ContractsRelease = _dii.l1ContractsRelease(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "op_contracts_manager"; + OPContractsManager opcm; - // This call contains a broadcast to deploy OPCM which is proxied. - OPContractsManager opcmProxy = createOPCMContract(_dii, _dio, blueprints, release, setters); + address existingImplementation = getReleaseAddress(l1ContractsRelease, contractName, stdVerToml); + if (existingImplementation != address(0)) { + opcm = OPContractsManager(existingImplementation); + } else { + // First we deploy the blueprints for the singletons deployed by OPCM. + // forgefmt: disable-start + bytes32 salt = _dii.salt(); + OPContractsManager.Blueprints memory blueprints; + + vm.startBroadcast(msg.sender); + blueprints.addressManager = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AddressManager")), salt); + blueprints.proxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("Proxy")), salt); + blueprints.proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt); + blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt); + blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt); + blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt); + (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt); + vm.stopBroadcast(); + // forgefmt: disable-end + + opcm = createOPCMContract(_dii, _dio, blueprints, l1ContractsRelease); + } - vm.label(address(opcmProxy), "OPContractsManager"); - _dio.set(_dio.opcmProxy.selector, address(opcmProxy)); + vm.label(address(opcm), "OPContractsManager"); + _dio.set(_dio.opcm.selector, address(opcm)); } // --- Core Contracts --- function deploySystemConfigImpl(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); // Using snake case for contract name to match the TOML file in superchain-registry. string memory contractName = "system_config"; @@ -659,7 +525,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = ISystemConfig(existingImplementation); - } else if (isDevelopRelease(release)) { + } else { // Deploy a new implementation for development builds. vm.broadcast(msg.sender); impl = ISystemConfig( @@ -668,8 +534,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "SystemConfigImpl"); @@ -683,7 +547,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "l1_cross_domain_messenger"; IL1CrossDomainMessenger impl; @@ -691,7 +555,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IL1CrossDomainMessenger(existingImplementation); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = IL1CrossDomainMessenger( DeployUtils.create1({ @@ -699,8 +563,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1CrossDomainMessenger.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "L1CrossDomainMessengerImpl"); @@ -714,7 +576,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "l1_erc721_bridge"; IL1ERC721Bridge impl; @@ -722,7 +584,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IL1ERC721Bridge(existingImplementation); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = IL1ERC721Bridge( DeployUtils.create1({ @@ -730,8 +592,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ERC721Bridge.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "L1ERC721BridgeImpl"); @@ -745,7 +605,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "l1_standard_bridge"; IL1StandardBridge impl; @@ -753,7 +613,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IL1StandardBridge(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = IL1StandardBridge( DeployUtils.create1({ @@ -761,8 +621,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1StandardBridge.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "L1StandardBridgeImpl"); @@ -776,7 +634,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "optimism_mintable_erc20_factory"; IOptimismMintableERC20Factory impl; @@ -784,7 +642,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IOptimismMintableERC20Factory(existingImplementation); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = IOptimismMintableERC20Factory( DeployUtils.create1({ @@ -792,32 +650,12 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismMintableERC20Factory.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "OptimismMintableERC20FactoryImpl"); _dio.set(_dio.optimismMintableERC20FactoryImpl.selector, address(impl)); } - function deployOPContractsManagerImpl( - DeployImplementationsInput _dii, - DeployImplementationsOutput _dio - ) - public - virtual - { - ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); - IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); - - vm.broadcast(msg.sender); - // TODO: Eventually we will want to select the correct implementation based on the release. - OPContractsManager impl = new OPContractsManager(superchainConfigProxy, protocolVersionsProxy); - - vm.label(address(impl), "OPContractsManagerImpl"); - _dio.set(_dio.opcmImpl.selector, address(impl)); - } - // --- Fault Proofs Contracts --- // The fault proofs contracts are configured as follows: @@ -862,7 +700,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "optimism_portal"; IOptimismPortal2 impl; @@ -870,7 +708,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IOptimismPortal2(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); vm.broadcast(msg.sender); @@ -884,8 +722,6 @@ contract DeployImplementations is Script { ) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "OptimismPortalImpl"); @@ -893,7 +729,7 @@ contract DeployImplementations is Script { } function deployDelayedWETHImpl(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "delayed_weth"; IDelayedWETH impl; @@ -901,7 +737,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IDelayedWETH(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { uint256 withdrawalDelaySeconds = _dii.withdrawalDelaySeconds(); vm.broadcast(msg.sender); impl = IDelayedWETH( @@ -912,8 +748,6 @@ contract DeployImplementations is Script { ) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "DelayedWETHImpl"); @@ -927,7 +761,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "preimage_oracle"; IPreimageOracle singleton; @@ -935,7 +769,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { singleton = IPreimageOracle(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { uint256 minProposalSizeBytes = _dii.minProposalSizeBytes(); uint256 challengePeriodSeconds = _dii.challengePeriodSeconds(); vm.broadcast(msg.sender); @@ -947,8 +781,6 @@ contract DeployImplementations is Script { ) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(singleton), "PreimageOracleSingleton"); @@ -956,7 +788,7 @@ contract DeployImplementations is Script { } function deployMipsSingleton(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "mips"; IMIPS singleton; @@ -964,7 +796,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { singleton = IMIPS(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { uint256 mipsVersion = _dii.mipsVersion(); IPreimageOracle preimageOracle = IPreimageOracle(address(_dio.preimageOracleSingleton())); vm.broadcast(msg.sender); @@ -974,8 +806,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(singleton), "MIPSSingleton"); @@ -989,7 +819,7 @@ contract DeployImplementations is Script { public virtual { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "dispute_game_factory"; IDisputeGameFactory impl; @@ -997,7 +827,7 @@ contract DeployImplementations is Script { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IDisputeGameFactory(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = IDisputeGameFactory( DeployUtils.create1({ @@ -1005,8 +835,6 @@ contract DeployImplementations is Script { _args: DeployUtils.encodeConstructor(abi.encodeCall(IDisputeGameFactory.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "DisputeGameFactoryImpl"); @@ -1076,11 +904,6 @@ contract DeployImplementations is Script { } } } - - // A release is considered a 'develop' release if it does not start with 'op-contracts'. - function isDevelopRelease(string memory _release) internal pure returns (bool) { - return !LibString.startsWith(_release, "op-contracts"); - } } // Similar to how DeploySuperchain.s.sol contains a lot of comments to thoroughly document the script @@ -1120,36 +943,35 @@ contract DeployImplementationsInterop is DeployImplementations { DeployImplementationsInput _dii, DeployImplementationsOutput _dio, OPContractsManager.Blueprints memory _blueprints, - string memory _release, - OPContractsManager.ImplementationSetter[] memory _setters + string memory _l1ContractsRelease ) internal + virtual override - returns (OPContractsManager opcmProxy_) + returns (OPContractsManager opcm_) { - address opcmProxyOwner = _dii.opcmProxyOwner(); + ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); + IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); + + OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({ + l1ERC721BridgeImpl: address(_dio.l1ERC721BridgeImpl()), + optimismPortalImpl: address(_dio.optimismPortalImpl()), + systemConfigImpl: address(_dio.systemConfigImpl()), + optimismMintableERC20FactoryImpl: address(_dio.optimismMintableERC20FactoryImpl()), + l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()), + l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()), + disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()), + delayedWETHImpl: address(_dio.delayedWETHImpl()), + mipsImpl: address(_dio.mipsSingleton()) + }); vm.broadcast(msg.sender); - IProxy proxy = IProxy( - DeployUtils.create1({ - _name: "Proxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) - }) + opcm_ = new OPContractsManagerInterop( + superchainConfigProxy, protocolVersionsProxy, _l1ContractsRelease, _blueprints, implementations ); - deployOPContractsManagerImpl(_dii, _dio); // overriding function - OPContractsManager opcmImpl = _dio.opcmImpl(); - - OPContractsManager.InitializerInputs memory initializerInputs = - OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); - - vm.startBroadcast(msg.sender); - proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); - - proxy.changeAdmin(opcmProxyOwner); // transfer ownership of Proxy contract to the ProxyAdmin contract - vm.stopBroadcast(); - - opcmProxy_ = OPContractsManagerInterop(address(proxy)); + vm.label(address(opcm_), "OPContractsManager"); + _dio.set(_dio.opcm.selector, address(opcm_)); } function deployOptimismPortalImpl( @@ -1159,7 +981,7 @@ contract DeployImplementationsInterop is DeployImplementations { public override { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "optimism_portal"; IOptimismPortalInterop impl; @@ -1167,7 +989,7 @@ contract DeployImplementationsInterop is DeployImplementations { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = IOptimismPortalInterop(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { + } else { uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); vm.broadcast(msg.sender); @@ -1182,8 +1004,6 @@ contract DeployImplementationsInterop is DeployImplementations { ) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "OptimismPortalImpl"); @@ -1197,7 +1017,7 @@ contract DeployImplementationsInterop is DeployImplementations { public override { - string memory release = _dii.release(); + string memory release = _dii.l1ContractsRelease(); string memory stdVerToml = _dii.standardVersionsToml(); string memory contractName = "system_config"; @@ -1206,7 +1026,7 @@ contract DeployImplementationsInterop is DeployImplementations { address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); if (existingImplementation != address(0)) { impl = ISystemConfigInterop(existingImplementation); - } else if (isDevelopRelease(release)) { + } else { vm.broadcast(msg.sender); impl = ISystemConfigInterop( DeployUtils.create1({ @@ -1214,46 +1034,9 @@ contract DeployImplementationsInterop is DeployImplementations { _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfigInterop.__constructor__, ())) }) ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); } vm.label(address(impl), "SystemConfigImpl"); _dio.set(_dio.systemConfigImpl.selector, address(impl)); } - - function deployOPContractsManagerImpl( - DeployImplementationsInput _dii, - DeployImplementationsOutput _dio - ) - public - override - { - ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); - IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); - - vm.broadcast(msg.sender); - // TODO: Eventually we will want to select the correct implementation based on the release. - OPContractsManager impl = new OPContractsManagerInterop(superchainConfigProxy, protocolVersionsProxy); - - vm.label(address(impl), "OPContractsManagerImpl"); - _dio.set(_dio.opcmImpl.selector, address(impl)); - } - - function opcmSystemConfigSetter( - DeployImplementationsInput, - DeployImplementationsOutput _dio - ) - internal - view - override - returns (OPContractsManager.ImplementationSetter memory) - { - return OPContractsManager.ImplementationSetter({ - name: "SystemConfig", - info: OPContractsManager.Implementation( - address(_dio.systemConfigImpl()), ISystemConfigInterop.initialize.selector - ) - }); - } } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index eb3b346452e9..a83dfb110625 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -47,7 +47,7 @@ contract DeployOPChainInput is BaseDeployIO { uint32 internal _basefeeScalar; uint32 internal _blobBaseFeeScalar; uint256 internal _l2ChainId; - OPContractsManager internal _opcmProxy; + OPContractsManager internal _opcm; string internal _saltMixer; uint64 internal _gasLimit; @@ -68,7 +68,7 @@ contract DeployOPChainInput is BaseDeployIO { else if (_sel == this.unsafeBlockSigner.selector) _unsafeBlockSigner = _addr; else if (_sel == this.proposer.selector) _proposer = _addr; else if (_sel == this.challenger.selector) _challenger = _addr; - else if (_sel == this.opcmProxy.selector) _opcmProxy = OPContractsManager(_addr); + else if (_sel == this.opcm.selector) _opcm = OPContractsManager(_addr); else revert("DeployOPChainInput: unknown selector"); } @@ -174,11 +174,10 @@ contract DeployOPChainInput is BaseDeployIO { return abi.encode(ScriptConstants.DEFAULT_STARTING_ANCHOR_ROOTS()); } - function opcmProxy() public returns (OPContractsManager) { - require(address(_opcmProxy) != address(0), "DeployOPChainInput: not set"); - DeployUtils.assertValidContractAddress(address(_opcmProxy)); - DeployUtils.assertERC1967ImplementationSet(address(_opcmProxy)); - return _opcmProxy; + function opcm() public view returns (OPContractsManager) { + require(address(_opcm) != address(0), "DeployOPChainInput: not set"); + DeployUtils.assertValidContractAddress(address(_opcm)); + return _opcm; } function saltMixer() public view returns (string memory) { @@ -347,7 +346,7 @@ contract DeployOPChain is Script { // -------- Core Deployment Methods -------- function run(DeployOPChainInput _doi, DeployOPChainOutput _doo) public { - OPContractsManager opcmProxy = _doi.opcmProxy(); + OPContractsManager opcm = _doi.opcm(); OPContractsManager.Roles memory roles = OPContractsManager.Roles({ opChainProxyAdminOwner: _doi.opChainProxyAdminOwner(), @@ -374,7 +373,7 @@ contract DeployOPChain is Script { }); vm.broadcast(msg.sender); - OPContractsManager.DeployOutput memory deployOutput = opcmProxy.deploy(deployInput); + OPContractsManager.DeployOutput memory deployOutput = opcm.deploy(deployInput); vm.label(address(deployOutput.opChainProxyAdmin), "opChainProxyAdmin"); vm.label(address(deployOutput.addressManager), "addressManager"); @@ -480,9 +479,9 @@ contract DeployOPChain is Script { "DPG-20" ); - OPContractsManager opcm = _doi.opcmProxy(); - (address mips,) = opcm.implementations(opcm.latestRelease(), "MIPS"); - require(game.vm() == IBigStepper(mips), "DPG-30"); + OPContractsManager opcm = _doi.opcm(); + address mipsImpl = opcm.implementations().mipsImpl; + require(game.vm() == IBigStepper(mipsImpl), "DPG-30"); require(address(game.weth()) == address(_doo.delayedWETHPermissionedGameProxy()), "DPG-40"); require(address(game.anchorStateRegistry()) == address(_doo.anchorStateRegistryProxy()), "DPG-50"); @@ -552,9 +551,7 @@ contract DeployOPChain is Script { require(outputConfig.maximumBaseFee == rConfig.maximumBaseFee, "SYSCON-130"); require(systemConfig.startBlock() == block.number, "SYSCON-140"); - require( - systemConfig.batchInbox() == _doi.opcmProxy().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SYSCON-150" - ); + require(systemConfig.batchInbox() == _doi.opcm().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SYSCON-150"); require(systemConfig.l1CrossDomainMessenger() == address(_doo.l1CrossDomainMessengerProxy()), "SYSCON-160"); require(systemConfig.l1ERC721Bridge() == address(_doo.l1ERC721BridgeProxy()), "SYSCON-170"); @@ -579,7 +576,7 @@ contract DeployOPChain is Script { require(address(messenger.PORTAL()) == address(_doo.optimismPortalProxy()), "L1xDM-30"); require(address(messenger.portal()) == address(_doo.optimismPortalProxy()), "L1xDM-40"); - require(address(messenger.superchainConfig()) == address(_doi.opcmProxy().superchainConfig()), "L1xDM-50"); + require(address(messenger.superchainConfig()) == address(_doi.opcm().superchainConfig()), "L1xDM-50"); bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204))); require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60"); @@ -595,7 +592,7 @@ contract DeployOPChain is Script { require(address(bridge.messenger()) == address(messenger), "L1SB-20"); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE, "L1SB-30"); require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE, "L1SB-40"); - require(address(bridge.superchainConfig()) == address(_doi.opcmProxy().superchainConfig()), "L1SB-50"); + require(address(bridge.superchainConfig()) == address(_doi.opcm().superchainConfig()), "L1SB-50"); } function assertValidOptimismMintableERC20Factory(DeployOPChainInput, DeployOPChainOutput _doo) internal { @@ -617,12 +614,12 @@ contract DeployOPChain is Script { require(address(bridge.MESSENGER()) == address(_doo.l1CrossDomainMessengerProxy()), "L721B-30"); require(address(bridge.messenger()) == address(_doo.l1CrossDomainMessengerProxy()), "L721B-40"); - require(address(bridge.superchainConfig()) == address(_doi.opcmProxy().superchainConfig()), "L721B-50"); + require(address(bridge.superchainConfig()) == address(_doi.opcm().superchainConfig()), "L721B-50"); } function assertValidOptimismPortal(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { IOptimismPortal2 portal = _doo.optimismPortalProxy(); - ISuperchainConfig superchainConfig = ISuperchainConfig(address(_doi.opcmProxy().superchainConfig())); + ISuperchainConfig superchainConfig = ISuperchainConfig(address(_doi.opcm().superchainConfig())); require(address(portal.disputeGameFactory()) == address(_doo.disputeGameFactoryProxy()), "PORTAL-10"); require(address(portal.systemConfig()) == address(_doo.systemConfigProxy()), "PORTAL-20"); diff --git a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol index 3992cf1cb657..35aefbed16d5 100644 --- a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol @@ -12,29 +12,18 @@ import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; import { IStaticL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol"; contract ReadImplementationAddressesInput is DeployOPChainOutput { - OPContractsManager internal _opcmProxy; - string internal _release; + OPContractsManager internal _opcm; function set(bytes4 _sel, address _addr) public override { require(_addr != address(0), "ReadImplementationAddressesInput: cannot set zero address"); - if (_sel == this.opcmProxy.selector) _opcmProxy = OPContractsManager(_addr); + if (_sel == this.opcm.selector) _opcm = OPContractsManager(_addr); else if (_sel == this.addressManager.selector) _addressManager = IAddressManager(_addr); else super.set(_sel, _addr); } - function set(bytes4 _sel, string memory _val) public { - if (_sel == this.release.selector) _release = _val; - else revert("ReadImplementationAddressesInput: unknown selector"); - } - - function opcmProxy() public view returns (OPContractsManager) { - DeployUtils.assertValidContractAddress(address(_opcmProxy)); - return _opcmProxy; - } - - function release() public view returns (string memory) { - require(bytes(_release).length != 0, "ReadImplementationAddressesInput: release not set"); - return _release; + function opcm() public view returns (OPContractsManager) { + DeployUtils.assertValidContractAddress(address(_opcm)); + return _opcm; } } @@ -154,9 +143,12 @@ contract ReadImplementationAddresses is Script { vm.prank(address(0)); _rio.set(_rio.l1StandardBridge.selector, l1SBImpl); - (address mipsLogic,) = _rii.opcmProxy().implementations(_rii.release(), "MIPS"); + address mipsLogic = _rii.opcm().implementations().mipsImpl; _rio.set(_rio.mipsSingleton.selector, mipsLogic); + address delayedWETH = _rii.opcm().implementations().delayedWETHImpl; + _rio.set(_rio.delayedWETH.selector, delayedWETH); + IAddressManager am = _rii.addressManager(); _rio.set(_rio.l1CrossDomainMessenger.selector, am.getAddress("OVM_L1CrossDomainMessenger")); diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 7c478feb235d..b5758eca610f 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -10,6 +10,110 @@ "internalType": "contract IProtocolVersions", "name": "_protocolVersions", "type": "address" + }, + { + "internalType": "string", + "name": "_l1ContractsRelease", + "type": "string" + }, + { + "components": [ + { + "internalType": "address", + "name": "addressManager", + "type": "address" + }, + { + "internalType": "address", + "name": "proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "proxyAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "l1ChugSplashProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "resolvedDelegateProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "anchorStateRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "permissionedDisputeGame1", + "type": "address" + }, + { + "internalType": "address", + "name": "permissionedDisputeGame2", + "type": "address" + } + ], + "internalType": "struct OPContractsManager.Blueprints", + "name": "_blueprints", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "l1ERC721BridgeImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismPortalImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "systemConfigImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismMintableERC20FactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1CrossDomainMessengerImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1StandardBridgeImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "disputeGameFactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "delayedWETHImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "mipsImpl", + "type": "address" + } + ], + "internalType": "struct OPContractsManager.Implementations", + "name": "_implementations", + "type": "tuple" } ], "stateMutability": "nonpayable", @@ -298,138 +402,68 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - }, - { - "internalType": "string", - "name": "", - "type": "string" - } - ], + "inputs": [], "name": "implementations", "outputs": [ - { - "internalType": "address", - "name": "logic", - "type": "address" - }, - { - "internalType": "bytes4", - "name": "initializer", - "type": "bytes4" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ { "components": [ { - "components": [ - { - "internalType": "address", - "name": "addressManager", - "type": "address" - }, - { - "internalType": "address", - "name": "proxy", - "type": "address" - }, - { - "internalType": "address", - "name": "proxyAdmin", - "type": "address" - }, - { - "internalType": "address", - "name": "l1ChugSplashProxy", - "type": "address" - }, - { - "internalType": "address", - "name": "resolvedDelegateProxy", - "type": "address" - }, - { - "internalType": "address", - "name": "anchorStateRegistry", - "type": "address" - }, - { - "internalType": "address", - "name": "permissionedDisputeGame1", - "type": "address" - }, - { - "internalType": "address", - "name": "permissionedDisputeGame2", - "type": "address" - } - ], - "internalType": "struct OPContractsManager.Blueprints", - "name": "blueprints", - "type": "tuple" + "internalType": "address", + "name": "l1ERC721BridgeImpl", + "type": "address" }, { - "components": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "components": [ - { - "internalType": "address", - "name": "logic", - "type": "address" - }, - { - "internalType": "bytes4", - "name": "initializer", - "type": "bytes4" - } - ], - "internalType": "struct OPContractsManager.Implementation", - "name": "info", - "type": "tuple" - } - ], - "internalType": "struct OPContractsManager.ImplementationSetter[]", - "name": "setters", - "type": "tuple[]" + "internalType": "address", + "name": "optimismPortalImpl", + "type": "address" }, { - "internalType": "string", - "name": "release", - "type": "string" + "internalType": "address", + "name": "systemConfigImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismMintableERC20FactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1CrossDomainMessengerImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1StandardBridgeImpl", + "type": "address" }, { - "internalType": "bool", - "name": "isLatest", - "type": "bool" + "internalType": "address", + "name": "disputeGameFactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "delayedWETHImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "mipsImpl", + "type": "address" } ], - "internalType": "struct OPContractsManager.InitializerInputs", - "name": "_initializerInputs", + "internalType": "struct OPContractsManager.Implementations", + "name": "", "type": "tuple" } ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "latestRelease", + "name": "l1ContractsRelease", "outputs": [ { "internalType": "string", @@ -529,19 +563,6 @@ "name": "Deployed", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, { "inputs": [ { diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json index 7c478feb235d..b5758eca610f 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerInterop.json @@ -10,6 +10,110 @@ "internalType": "contract IProtocolVersions", "name": "_protocolVersions", "type": "address" + }, + { + "internalType": "string", + "name": "_l1ContractsRelease", + "type": "string" + }, + { + "components": [ + { + "internalType": "address", + "name": "addressManager", + "type": "address" + }, + { + "internalType": "address", + "name": "proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "proxyAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "l1ChugSplashProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "resolvedDelegateProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "anchorStateRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "permissionedDisputeGame1", + "type": "address" + }, + { + "internalType": "address", + "name": "permissionedDisputeGame2", + "type": "address" + } + ], + "internalType": "struct OPContractsManager.Blueprints", + "name": "_blueprints", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "l1ERC721BridgeImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismPortalImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "systemConfigImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismMintableERC20FactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1CrossDomainMessengerImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1StandardBridgeImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "disputeGameFactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "delayedWETHImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "mipsImpl", + "type": "address" + } + ], + "internalType": "struct OPContractsManager.Implementations", + "name": "_implementations", + "type": "tuple" } ], "stateMutability": "nonpayable", @@ -298,138 +402,68 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - }, - { - "internalType": "string", - "name": "", - "type": "string" - } - ], + "inputs": [], "name": "implementations", "outputs": [ - { - "internalType": "address", - "name": "logic", - "type": "address" - }, - { - "internalType": "bytes4", - "name": "initializer", - "type": "bytes4" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ { "components": [ { - "components": [ - { - "internalType": "address", - "name": "addressManager", - "type": "address" - }, - { - "internalType": "address", - "name": "proxy", - "type": "address" - }, - { - "internalType": "address", - "name": "proxyAdmin", - "type": "address" - }, - { - "internalType": "address", - "name": "l1ChugSplashProxy", - "type": "address" - }, - { - "internalType": "address", - "name": "resolvedDelegateProxy", - "type": "address" - }, - { - "internalType": "address", - "name": "anchorStateRegistry", - "type": "address" - }, - { - "internalType": "address", - "name": "permissionedDisputeGame1", - "type": "address" - }, - { - "internalType": "address", - "name": "permissionedDisputeGame2", - "type": "address" - } - ], - "internalType": "struct OPContractsManager.Blueprints", - "name": "blueprints", - "type": "tuple" + "internalType": "address", + "name": "l1ERC721BridgeImpl", + "type": "address" }, { - "components": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "components": [ - { - "internalType": "address", - "name": "logic", - "type": "address" - }, - { - "internalType": "bytes4", - "name": "initializer", - "type": "bytes4" - } - ], - "internalType": "struct OPContractsManager.Implementation", - "name": "info", - "type": "tuple" - } - ], - "internalType": "struct OPContractsManager.ImplementationSetter[]", - "name": "setters", - "type": "tuple[]" + "internalType": "address", + "name": "optimismPortalImpl", + "type": "address" }, { - "internalType": "string", - "name": "release", - "type": "string" + "internalType": "address", + "name": "systemConfigImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismMintableERC20FactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1CrossDomainMessengerImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "l1StandardBridgeImpl", + "type": "address" }, { - "internalType": "bool", - "name": "isLatest", - "type": "bool" + "internalType": "address", + "name": "disputeGameFactoryImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "delayedWETHImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "mipsImpl", + "type": "address" } ], - "internalType": "struct OPContractsManager.InitializerInputs", - "name": "_initializerInputs", + "internalType": "struct OPContractsManager.Implementations", + "name": "", "type": "tuple" } ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "latestRelease", + "name": "l1ContractsRelease", "outputs": [ { "internalType": "string", @@ -529,19 +563,6 @@ "name": "Deployed", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, { "inputs": [ { diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index e96fa687e9c6..8f40dca3ff43 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -20,8 +20,8 @@ "sourceCodeHash": "0x4132ff37d267cb12224b75ea806c0aa7d25407b0d66ce526d7fcda8f7d223882" }, "src/L1/OPContractsManager.sol": { - "initCodeHash": "0xd58cb3978affc5c1457cdd498ff8420c90aef804d4c3b62cf42ab2691986d6d2", - "sourceCodeHash": "0x7bfa6eff76176649fe600303cd60009a0f6e282cbaec55836b5ea1f8875cbeb5" + "initCodeHash": "0xd038cc35325d023499151264232d75fa4ecc81f04a8c8353e6b50c43af224d6e", + "sourceCodeHash": "0xa13f3ab2b8744015290dbabe5f20fdd44774607e6a7ad3e5e016303fc4aa8c12" }, "src/L1/OptimismPortal.sol": { "initCodeHash": "0x152167cfa18635ae4918a6eb3371a599cfa084418c0a652799cdb48bfc0ee0cc", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManager.json b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManager.json index aeef539c5c20..aa8148b34cba 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManager.json @@ -1,51 +1,30 @@ [ - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, { "bytes": "32", - "label": "latestRelease", + "label": "l1ContractsRelease", "offset": 0, - "slot": "1", + "slot": "0", "type": "string" }, - { - "bytes": "32", - "label": "implementations", - "offset": 0, - "slot": "2", - "type": "mapping(string => mapping(string => struct OPContractsManager.Implementation))" - }, { "bytes": "32", "label": "systemConfigs", "offset": 0, - "slot": "3", + "slot": "1", "type": "mapping(uint256 => contract ISystemConfig)" }, { "bytes": "256", "label": "blueprint", "offset": 0, - "slot": "4", + "slot": "2", "type": "struct OPContractsManager.Blueprints" }, { - "bytes": "1600", - "label": "__gap", + "bytes": "288", + "label": "implementation", "offset": 0, - "slot": "12", - "type": "uint256[50]" + "slot": "10", + "type": "struct OPContractsManager.Implementations" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerInterop.json index aeef539c5c20..aa8148b34cba 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerInterop.json @@ -1,51 +1,30 @@ [ - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, { "bytes": "32", - "label": "latestRelease", + "label": "l1ContractsRelease", "offset": 0, - "slot": "1", + "slot": "0", "type": "string" }, - { - "bytes": "32", - "label": "implementations", - "offset": 0, - "slot": "2", - "type": "mapping(string => mapping(string => struct OPContractsManager.Implementation))" - }, { "bytes": "32", "label": "systemConfigs", "offset": 0, - "slot": "3", + "slot": "1", "type": "mapping(uint256 => contract ISystemConfig)" }, { "bytes": "256", "label": "blueprint", "offset": 0, - "slot": "4", + "slot": "2", "type": "struct OPContractsManager.Blueprints" }, { - "bytes": "1600", - "label": "__gap", + "bytes": "288", + "label": "implementation", "offset": 0, - "slot": "12", - "type": "uint256[50]" + "slot": "10", + "type": "struct OPContractsManager.Implementations" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 4bf52ff228a1..a2a5a5f215b0 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -4,15 +4,12 @@ pragma solidity 0.8.15; import { Blueprint } from "src/libraries/Blueprint.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; - import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; -import { ISystemConfigV160 } from "src/L1/interfaces/ISystemConfigV160.sol"; import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; @@ -28,14 +25,12 @@ import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; -import { ISystemConfigV160 } from "src/L1/interfaces/ISystemConfigV160.sol"; import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMessenger.sol"; import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; import { IOptimismMintableERC20Factory } from "src/universal/interfaces/IOptimismMintableERC20Factory.sol"; -/// @custom:proxied true -contract OPContractsManager is ISemver, Initializable { +contract OPContractsManager is ISemver { // -------- Structs -------- /// @notice Represents the roles that can be set when deploying a standard OP Stack chain. @@ -89,19 +84,6 @@ contract OPContractsManager is ISemver, Initializable { IDelayedWETH delayedWETHPermissionlessGameProxy; } - /// @notice The logic address and initializer selector for an implementation contract. - struct Implementation { - address logic; // Address containing the deployed logic contract. - bytes4 initializer; // Function selector for the initializer. - } - - /// @notice Used to set the implementation for a contract by mapping a contract - /// name to the implementation data. - struct ImplementationSetter { - string name; // Contract name. - Implementation info; // Implementation to set. - } - /// @notice Addresses of ERC-5202 Blueprint contracts. There are used for deploying full size /// contracts, to reduce the code size of this factory contract. If it deployed full contracts /// using the `new Proxy()` syntax, the code size would get large fast, since this contract would @@ -118,19 +100,23 @@ contract OPContractsManager is ISemver, Initializable { address permissionedDisputeGame2; } - /// @notice Inputs required when initializing the OPContractsManager. To avoid 'StackTooDeep' errors, - /// all necessary inputs (excluding immutables) for initialization are bundled together in this struct. - struct InitializerInputs { - Blueprints blueprints; - ImplementationSetter[] setters; - string release; - bool isLatest; + /// @notice The latest implementation contracts for the OP Stack. + struct Implementations { + address l1ERC721BridgeImpl; + address optimismPortalImpl; + address systemConfigImpl; + address optimismMintableERC20FactoryImpl; + address l1CrossDomainMessengerImpl; + address l1StandardBridgeImpl; + address disputeGameFactoryImpl; + address delayedWETHImpl; + address mipsImpl; } // -------- Constants and Variables -------- - /// @custom:semver 1.0.0-beta.20 - string public constant version = "1.0.0-beta.20"; + /// @custom:semver 1.0.0-beta.21 + string public constant version = "1.0.0-beta.21"; /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. @@ -142,24 +128,20 @@ contract OPContractsManager is ISemver, Initializable { /// @notice Address of the ProtocolVersions contract shared by all chains. IProtocolVersions public immutable protocolVersions; - /// @notice The latest release of the OP Contracts Manager, as a string of the format `op-contracts/vX.Y.Z`. - string public latestRelease; - - /// @notice Maps a release version to a contract name to it's implementation data. - mapping(string => mapping(string => Implementation)) public implementations; + // @notice L1 smart contracts release deployed by this version of OPCM. This is used in opcm to signal which version + // of the L1 smart contracts is deployed. It takes the format of `op-contracts/vX.Y.Z`. + string public l1ContractsRelease; /// @notice Maps an L2 Chain ID to the SystemConfig for that chain. mapping(uint256 => ISystemConfig) public systemConfigs; /// @notice Addresses of the Blueprint contracts. /// This is internal because if public the autogenerated getter method would return a tuple of - /// addresses, but we want it to return a struct. This is also set via `initialize` because - /// we can't make this an immutable variable as it is a non-value type. + /// addresses, but we want it to return a struct. Blueprints internal blueprint; - /// @notice Storage gap for future modifications, so we can expand the number of blueprints - /// without affecting other storage variables. - uint256[50] private __gap; + /// @notice Addresses of the latest implementation contracts. + Implementations internal implementation; // -------- Events -------- @@ -197,37 +179,26 @@ contract OPContractsManager is ISemver, Initializable { // -------- Methods -------- - /// @notice OPCM is proxied. Therefore the `initialize` function replaces most constructor logic for this contract. - - constructor(ISuperchainConfig _superchainConfig, IProtocolVersions _protocolVersions) { + constructor( + ISuperchainConfig _superchainConfig, + IProtocolVersions _protocolVersions, + string memory _l1ContractsRelease, + Blueprints memory _blueprints, + Implementations memory _implementations + ) { assertValidContractAddress(address(_superchainConfig)); assertValidContractAddress(address(_protocolVersions)); superchainConfig = _superchainConfig; protocolVersions = _protocolVersions; - _disableInitializers(); - } - - function initialize(InitializerInputs memory _initializerInputs) public initializer { - if (_initializerInputs.isLatest) latestRelease = _initializerInputs.release; - if (keccak256(bytes(latestRelease)) == keccak256("")) revert LatestReleaseNotSet(); + l1ContractsRelease = _l1ContractsRelease; - for (uint256 i = 0; i < _initializerInputs.setters.length; i++) { - ImplementationSetter memory setter = _initializerInputs.setters[i]; - Implementation storage impl = implementations[_initializerInputs.release][setter.name]; - if (impl.logic != address(0)) revert AlreadyReleased(); - - impl.initializer = setter.info.initializer; - impl.logic = setter.info.logic; - } - - blueprint = _initializerInputs.blueprints; + blueprint = _blueprints; + implementation = _implementations; } function deploy(DeployInput calldata _input) external returns (DeployOutput memory) { assertValidInputs(_input); - uint256 l2ChainId = _input.l2ChainId; - // The salt for a non-proxy contract is a function of the chain ID and the salt mixer. string memory saltMixer = _input.saltMixer; bytes32 salt = keccak256(abi.encode(l2ChainId, saltMixer)); @@ -266,7 +237,6 @@ contract OPContractsManager is ISemver, Initializable { payable(Blueprint.deployFrom(blueprint.l1ChugSplashProxy, salt, abi.encode(output.opChainProxyAdmin))) ); output.opChainProxyAdmin.setProxyType(address(output.l1StandardBridgeProxy), IProxyAdmin.ProxyType.CHUGSPLASH); - string memory contractName = "OVM_L1CrossDomainMessenger"; output.l1CrossDomainMessengerProxy = IL1CrossDomainMessenger( Blueprint.deployFrom(blueprint.resolvedDelegateProxy, salt, abi.encode(output.addressManager, contractName)) @@ -275,10 +245,8 @@ contract OPContractsManager is ISemver, Initializable { address(output.l1CrossDomainMessengerProxy), IProxyAdmin.ProxyType.RESOLVED ); output.opChainProxyAdmin.setImplementationName(address(output.l1CrossDomainMessengerProxy), contractName); - // Now that all proxies are deployed, we can transfer ownership of the AddressManager to the ProxyAdmin. output.addressManager.transferOwnership(address(output.opChainProxyAdmin)); - // The AnchorStateRegistry Implementation is not MCP Ready, and therefore requires an implementation per chain. // It must be deployed after the DisputeGameFactoryProxy so that it can be provided as a constructor argument. output.anchorStateRegistryImpl = IAnchorStateRegistry( @@ -301,54 +269,76 @@ contract OPContractsManager is ISemver, Initializable { ); // -------- Set and Initialize Proxy Implementations -------- - Implementation memory impl; bytes memory data; - impl = getLatestImplementation("L1ERC721Bridge"); - data = encodeL1ERC721BridgeInitializer(impl.initializer, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), impl.logic, data); + data = encodeL1ERC721BridgeInitializer(IL1ERC721Bridge.initialize.selector, output); + upgradeAndCall( + output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data + ); - impl = getLatestImplementation("OptimismPortal"); - data = encodeOptimismPortalInitializer(impl.initializer, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.optimismPortalProxy), impl.logic, data); + data = encodeOptimismPortalInitializer(IOptimismPortal2.initialize.selector, output); + upgradeAndCall( + output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data + ); // First we upgrade the implementation so it's version can be retrieved, then we initialize // it afterwards. See the comments in encodeSystemConfigInitializer to learn more. - impl = getLatestImplementation("SystemConfig"); - output.opChainProxyAdmin.upgrade(payable(address(output.systemConfigProxy)), impl.logic); - data = encodeSystemConfigInitializer(impl.initializer, _input, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.systemConfigProxy), impl.logic, data); + output.opChainProxyAdmin.upgrade(payable(address(output.systemConfigProxy)), implementation.systemConfigImpl); + data = encodeSystemConfigInitializer(_input, output); + upgradeAndCall( + output.opChainProxyAdmin, address(output.systemConfigProxy), implementation.systemConfigImpl, data + ); - impl = getLatestImplementation("OptimismMintableERC20Factory"); - data = encodeOptimismMintableERC20FactoryInitializer(impl.initializer, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.optimismMintableERC20FactoryProxy), impl.logic, data); + data = encodeOptimismMintableERC20FactoryInitializer(IOptimismMintableERC20Factory.initialize.selector, output); + upgradeAndCall( + output.opChainProxyAdmin, + address(output.optimismMintableERC20FactoryProxy), + implementation.optimismMintableERC20FactoryImpl, + data + ); - impl = getLatestImplementation("L1CrossDomainMessenger"); - data = encodeL1CrossDomainMessengerInitializer(impl.initializer, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.l1CrossDomainMessengerProxy), impl.logic, data); + data = encodeL1CrossDomainMessengerInitializer(IL1CrossDomainMessenger.initialize.selector, output); + upgradeAndCall( + output.opChainProxyAdmin, + address(output.l1CrossDomainMessengerProxy), + implementation.l1CrossDomainMessengerImpl, + data + ); - impl = getLatestImplementation("L1StandardBridge"); - data = encodeL1StandardBridgeInitializer(impl.initializer, output); - upgradeAndCall(output.opChainProxyAdmin, address(output.l1StandardBridgeProxy), impl.logic, data); + data = encodeL1StandardBridgeInitializer(IL1StandardBridge.initialize.selector, output); + upgradeAndCall( + output.opChainProxyAdmin, address(output.l1StandardBridgeProxy), implementation.l1StandardBridgeImpl, data + ); - impl = getLatestImplementation("DelayedWETH"); - data = encodeDelayedWETHInitializer(impl.initializer, _input); + data = encodeDelayedWETHInitializer(IDelayedWETH.initialize.selector, _input); // Eventually we will switch from DelayedWETHPermissionedGameProxy to DelayedWETHPermissionlessGameProxy. - upgradeAndCall(output.opChainProxyAdmin, address(output.delayedWETHPermissionedGameProxy), impl.logic, data); + upgradeAndCall( + output.opChainProxyAdmin, + address(output.delayedWETHPermissionedGameProxy), + implementation.delayedWETHImpl, + data + ); // We set the initial owner to this contract, set game implementations, then transfer ownership. - impl = getLatestImplementation("DisputeGameFactory"); - data = encodeDisputeGameFactoryInitializer(impl.initializer, _input); - upgradeAndCall(output.opChainProxyAdmin, address(output.disputeGameFactoryProxy), impl.logic, data); + data = encodeDisputeGameFactoryInitializer(IDisputeGameFactory.initialize.selector, _input); + upgradeAndCall( + output.opChainProxyAdmin, + address(output.disputeGameFactoryProxy), + implementation.disputeGameFactoryImpl, + data + ); output.disputeGameFactoryProxy.setImplementation( GameTypes.PERMISSIONED_CANNON, IDisputeGame(address(output.permissionedDisputeGame)) ); output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner)); - impl.logic = address(output.anchorStateRegistryImpl); - impl.initializer = IAnchorStateRegistry.initialize.selector; - data = encodeAnchorStateRegistryInitializer(impl.initializer, _input); - upgradeAndCall(output.opChainProxyAdmin, address(output.anchorStateRegistryProxy), impl.logic, data); + data = encodeAnchorStateRegistryInitializer(IAnchorStateRegistry.initialize.selector, _input); + upgradeAndCall( + output.opChainProxyAdmin, + address(output.anchorStateRegistryProxy), + address(output.anchorStateRegistryImpl), + data + ); // -------- Finalize Deployment -------- // Transfer ownership of the ProxyAdmin from this contract to the specified owner. @@ -402,13 +392,6 @@ contract OPContractsManager is ISemver, Initializable { return Blueprint.deployFrom(blueprint.proxy, salt, abi.encode(_proxyAdmin)); } - /// @notice Returns the implementation data for a contract name. Makes a copy of the internal - // Implementation struct in storage to prevent accidental mutation of the internal data. - function getLatestImplementation(string memory _name) internal view returns (Implementation memory) { - Implementation storage impl = implementations[latestRelease][_name]; - return Implementation({ logic: impl.logic, initializer: impl.initializer }); - } - // -------- Initializer Encoding -------- /// @notice Helper method for encoding the L1ERC721Bridge initializer data. @@ -445,7 +428,6 @@ contract OPContractsManager is ISemver, Initializable { /// @notice Helper method for encoding the SystemConfig initializer data. function encodeSystemConfigInitializer( - bytes4 _selector, DeployInput memory _input, DeployOutput memory _output ) @@ -454,50 +436,22 @@ contract OPContractsManager is ISemver, Initializable { virtual returns (bytes memory) { - // We inspect the SystemConfig contract and determine it's signature here. This is required - // because this OPCM contract is being developed in a repository that no longer contains the - // SystemConfig contract that was released as part of `op-contracts/v1.6.0`, but in production - // it needs to support that version, in addition to the version currently on develop. - string memory semver = _output.systemConfigProxy.version(); - if (keccak256(abi.encode(semver)) == keccak256(abi.encode(string("2.2.0")))) { - // We are using the op-contracts/v1.6.0 SystemConfig contract. - ( - IResourceMetering.ResourceConfig memory referenceResourceConfig, - ISystemConfigV160.Addresses memory opChainAddrs - ) = defaultSystemConfigV160Params(_selector, _input, _output); - - return abi.encodeWithSelector( - _selector, - _input.roles.systemConfigOwner, - _input.basefeeScalar, - _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash - _input.gasLimit, - _input.roles.unsafeBlockSigner, - referenceResourceConfig, - chainIdToBatchInboxAddress(_input.l2ChainId), - opChainAddrs - ); - } else { - // We are using the latest SystemConfig contract from the repo. - ( - IResourceMetering.ResourceConfig memory referenceResourceConfig, - ISystemConfig.Addresses memory opChainAddrs - ) = defaultSystemConfigParams(_selector, _input, _output); - - return abi.encodeWithSelector( - _selector, - _input.roles.systemConfigOwner, - _input.basefeeScalar, - _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash - _input.gasLimit, - _input.roles.unsafeBlockSigner, - referenceResourceConfig, - chainIdToBatchInboxAddress(_input.l2ChainId), - opChainAddrs - ); - } + bytes4 selector = ISystemConfig.initialize.selector; + (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = + defaultSystemConfigParams(selector, _input, _output); + + return abi.encodeWithSelector( + selector, + _input.roles.systemConfigOwner, + _input.basefeeScalar, + _input.blobBasefeeScalar, + bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash + _input.gasLimit, + _input.roles.unsafeBlockSigner, + referenceResourceConfig, + chainIdToBatchInboxAddress(_input.l2ChainId), + opChainAddrs + ); } /// @notice Helper method for encoding the OptimismMintableERC20Factory initializer data. @@ -599,7 +553,7 @@ contract OPContractsManager is ISemver, Initializable { _input.disputeSplitDepth, _input.disputeClockExtension, _input.disputeMaxClockDuration, - IBigStepper(getLatestImplementation("MIPS").logic), + IBigStepper(implementation.mipsImpl), IDelayedWETH(payable(address(_output.delayedWETHPermissionedGameProxy))), IAnchorStateRegistry(address(_output.anchorStateRegistryProxy)), _input.l2ChainId, @@ -645,45 +599,6 @@ contract OPContractsManager is ISemver, Initializable { assertValidContractAddress(opChainAddrs_.optimismMintableERC20Factory); } - /// @notice Returns default, standard config arguments for the SystemConfig initializer. - /// This is used by subclasses to reduce code duplication. - function defaultSystemConfigV160Params( - bytes4, /* selector */ - DeployInput memory, /* _input */ - DeployOutput memory _output - ) - internal - view - virtual - returns ( - IResourceMetering.ResourceConfig memory resourceConfig_, - ISystemConfigV160.Addresses memory opChainAddrs_ - ) - { - // We use assembly to easily convert from IResourceMetering.ResourceConfig to ResourceMetering.ResourceConfig. - // This is required because we have not yet fully migrated the codebase to be interface-based. - IResourceMetering.ResourceConfig memory resourceConfig = Constants.DEFAULT_RESOURCE_CONFIG(); - assembly ("memory-safe") { - resourceConfig_ := resourceConfig - } - - opChainAddrs_ = ISystemConfigV160.Addresses({ - l1CrossDomainMessenger: address(_output.l1CrossDomainMessengerProxy), - l1ERC721Bridge: address(_output.l1ERC721BridgeProxy), - l1StandardBridge: address(_output.l1StandardBridgeProxy), - disputeGameFactory: address(_output.disputeGameFactoryProxy), - optimismPortal: address(_output.optimismPortalProxy), - optimismMintableERC20Factory: address(_output.optimismMintableERC20FactoryProxy) - }); - - assertValidContractAddress(opChainAddrs_.l1CrossDomainMessenger); - assertValidContractAddress(opChainAddrs_.l1ERC721Bridge); - assertValidContractAddress(opChainAddrs_.l1StandardBridge); - assertValidContractAddress(opChainAddrs_.disputeGameFactory); - assertValidContractAddress(opChainAddrs_.optimismPortal); - assertValidContractAddress(opChainAddrs_.optimismMintableERC20Factory); - } - /// @notice Makes an external call to the target to initialize the proxy with the specified data. /// First performs safety checks to ensure the target, implementation, and proxy admin are valid. function upgradeAndCall( @@ -710,4 +625,9 @@ contract OPContractsManager is ISemver, Initializable { function blueprints() public view returns (Blueprints memory) { return blueprint; } + + /// @notice Returns the implementation contract addresses. + function implementations() public view returns (Implementations memory) { + return implementation; + } } diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol index 19de5537b41c..79e0efda9735 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol @@ -6,20 +6,22 @@ import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; +import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol"; -/// @custom:proxied true contract OPContractsManagerInterop is OPContractsManager { constructor( ISuperchainConfig _superchainConfig, - IProtocolVersions _protocolVersions + IProtocolVersions _protocolVersions, + string memory _l1ContractsRelease, + Blueprints memory _blueprints, + Implementations memory _implementations ) - OPContractsManager(_superchainConfig, _protocolVersions) + OPContractsManager(_superchainConfig, _protocolVersions, _l1ContractsRelease, _blueprints, _implementations) { } // The `SystemConfigInterop` contract has an extra `address _dependencyManager` argument // that we must account for. function encodeSystemConfigInitializer( - bytes4 _selector, DeployInput memory _input, DeployOutput memory _output ) @@ -29,8 +31,9 @@ contract OPContractsManagerInterop is OPContractsManager { override returns (bytes memory) { + bytes4 selector = ISystemConfigInterop.initialize.selector; (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = - defaultSystemConfigParams(_selector, _input, _output); + defaultSystemConfigParams(selector, _input, _output); // TODO For now we assume that the dependency manager is the same as system config owner. // This is currently undefined since it's not part of the standard config, so we may need @@ -40,7 +43,7 @@ contract OPContractsManagerInterop is OPContractsManager { address dependencyManager = address(_input.roles.systemConfigOwner); return abi.encodeWithSelector( - _selector, + selector, _input.roles.systemConfigOwner, _input.basefeeScalar, _input.blobBasefeeScalar, diff --git a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigV160.sol b/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigV160.sol deleted file mode 100644 index 210b0ddf8e5e..000000000000 --- a/packages/contracts-bedrock/src/L1/interfaces/ISystemConfigV160.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; - -/// @notice This interface corresponds to the op-contracts/v1.6.0 release of the SystemConfig -/// contract, which has a semver of 2.2.0 as specified in -/// https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.6.0 -interface ISystemConfigV160 { - enum UpdateType { - BATCHER, - FEE_SCALARS, - GAS_LIMIT, - UNSAFE_BLOCK_SIGNER - } - - struct Addresses { - address l1CrossDomainMessenger; - address l1ERC721Bridge; - address l1StandardBridge; - address disputeGameFactory; - address optimismPortal; - address optimismMintableERC20Factory; - } - - event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); - event Initialized(uint8 version); - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - function BATCH_INBOX_SLOT() external view returns (bytes32); - function DISPUTE_GAME_FACTORY_SLOT() external view returns (bytes32); - function L1_CROSS_DOMAIN_MESSENGER_SLOT() external view returns (bytes32); - function L1_ERC_721_BRIDGE_SLOT() external view returns (bytes32); - function L1_STANDARD_BRIDGE_SLOT() external view returns (bytes32); - function OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT() external view returns (bytes32); - function OPTIMISM_PORTAL_SLOT() external view returns (bytes32); - function START_BLOCK_SLOT() external view returns (bytes32); - function UNSAFE_BLOCK_SIGNER_SLOT() external view returns (bytes32); - function VERSION() external view returns (uint256); - function basefeeScalar() external view returns (uint32); - function batchInbox() external view returns (address addr_); - function batcherHash() external view returns (bytes32); - function blobbasefeeScalar() external view returns (uint32); - function disputeGameFactory() external view returns (address addr_); - function gasLimit() external view returns (uint64); - function gasPayingToken() external view returns (address addr_, uint8 decimals_); - function gasPayingTokenName() external view returns (string memory name_); - function gasPayingTokenSymbol() external view returns (string memory symbol_); - function initialize( - address _owner, - uint256 _basefeeScalar, - uint256 _blobbasefeeScalar, - bytes32 _batcherHash, - uint64 _gasLimit, - address _unsafeBlockSigner, - IResourceMetering.ResourceConfig memory _config, - address _batchInbox, - Addresses memory _addresses - ) - external; - function isCustomGasToken() external view returns (bool); - function l1CrossDomainMessenger() external view returns (address addr_); - function l1ERC721Bridge() external view returns (address addr_); - function l1StandardBridge() external view returns (address addr_); - function maximumGasLimit() external pure returns (uint64); - function minimumGasLimit() external view returns (uint64); - function optimismMintableERC20Factory() external view returns (address addr_); - function optimismPortal() external view returns (address addr_); - function overhead() external view returns (uint256); - function owner() external view returns (address); - function renounceOwnership() external; - function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); - function scalar() external view returns (uint256); - function setBatcherHash(bytes32 _batcherHash) external; - function setGasConfig(uint256 _overhead, uint256 _scalar) external; - function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external; - function setGasLimit(uint64 _gasLimit) external; - function setUnsafeBlockSigner(address _unsafeBlockSigner) external; - function startBlock() external view returns (uint256 startBlock_); - function transferOwnership(address newOwner) external; // nosemgrep - function unsafeBlockSigner() external view returns (address addr_); - function version() external pure returns (string memory); - - function __constructor__() external; -} diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 5b2260fce992..e828b415621e 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -14,9 +14,12 @@ import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; contract OPContractsManager_Harness is OPContractsManager { constructor( ISuperchainConfig _superchainConfig, - IProtocolVersions _protocolVersions + IProtocolVersions _protocolVersions, + string memory _l1ContractsRelease, + Blueprints memory _blueprints, + Implementations memory _implementations ) - OPContractsManager(_superchainConfig, _protocolVersions) + OPContractsManager(_superchainConfig, _protocolVersions, _l1ContractsRelease, _blueprints, _implementations) { } function chainIdToBatchInboxAddress_exposed(uint256 l2ChainId) public pure returns (address) { @@ -49,7 +52,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { doi.set(doi.basefeeScalar.selector, basefeeScalar); doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); - doi.set(doi.opcmProxy.selector, address(opcm)); + doi.set(doi.opcm.selector, address(opcm)); doi.set(doi.gasLimit.selector, gasLimit); doi.set(doi.disputeGameType.selector, disputeGameType); @@ -116,12 +119,17 @@ contract OPContractsManager_InternalMethods_Test is Test { function setUp() public { ISuperchainConfig superchainConfigProxy = ISuperchainConfig(makeAddr("superchainConfig")); IProtocolVersions protocolVersionsProxy = IProtocolVersions(makeAddr("protocolVersions")); + OPContractsManager.Blueprints memory emptyBlueprints; + OPContractsManager.Implementations memory emptyImpls; vm.etch(address(superchainConfigProxy), hex"01"); vm.etch(address(protocolVersionsProxy), hex"01"); opcmHarness = new OPContractsManager_Harness({ _superchainConfig: superchainConfigProxy, - _protocolVersions: protocolVersionsProxy + _protocolVersions: protocolVersionsProxy, + _l1ContractsRelease: "dev", + _blueprints: emptyBlueprints, + _implementations: emptyImpls }); } diff --git a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol index 0282ea0b3d90..8d3feac2de0d 100644 --- a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol @@ -61,7 +61,7 @@ contract DeployImplementationsInput_Test is Test { dii.disputeGameFinalityDelaySeconds(); vm.expectRevert("DeployImplementationsInput: not set"); - dii.release(); + dii.l1ContractsRelease(); vm.expectRevert("DeployImplementationsInput: not set"); dii.superchainConfigProxy(); @@ -69,23 +69,9 @@ contract DeployImplementationsInput_Test is Test { vm.expectRevert("DeployImplementationsInput: not set"); dii.protocolVersionsProxy(); - vm.expectRevert("DeployImplementationsInput: not set"); - dii.opcmProxyOwner(); - vm.expectRevert("DeployImplementationsInput: not set"); dii.standardVersionsToml(); } - - function test_opcmProxyOwner_whenNotSet_reverts() public { - vm.expectRevert("DeployImplementationsInput: not set"); - dii.opcmProxyOwner(); - } - - function test_opcmProxyOwner_succeeds() public { - dii.set(dii.opcmProxyOwner.selector, address(msg.sender)); - address opcmProxyOwner = dii.opcmProxyOwner(); - assertEq(address(msg.sender), address(opcmProxyOwner), "100"); - } } contract DeployImplementationsOutput_Test is Test { @@ -96,17 +82,7 @@ contract DeployImplementationsOutput_Test is Test { } function test_set_succeeds() public { - IProxy proxy = IProxy( - DeployUtils.create1({ - _name: "Proxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(0)))) - }) - ); - address opcmImpl = address(makeAddr("opcmImpl")); - vm.prank(address(0)); - proxy.upgradeTo(opcmImpl); - - OPContractsManager opcmProxy = OPContractsManager(address(proxy)); + OPContractsManager opcm = OPContractsManager(address(makeAddr("opcm"))); IOptimismPortal2 optimismPortalImpl = IOptimismPortal2(payable(makeAddr("optimismPortalImpl"))); IDelayedWETH delayedWETHImpl = IDelayedWETH(payable(makeAddr("delayedWETHImpl"))); IPreimageOracle preimageOracleSingleton = IPreimageOracle(makeAddr("preimageOracleSingleton")); @@ -120,8 +96,7 @@ contract DeployImplementationsOutput_Test is Test { IOptimismMintableERC20Factory(makeAddr("optimismMintableERC20FactoryImpl")); IDisputeGameFactory disputeGameFactoryImpl = IDisputeGameFactory(makeAddr("disputeGameFactoryImpl")); - vm.etch(address(opcmProxy), address(opcmProxy).code); - vm.etch(address(opcmImpl), hex"01"); + vm.etch(address(opcm), hex"01"); vm.etch(address(optimismPortalImpl), hex"01"); vm.etch(address(delayedWETHImpl), hex"01"); vm.etch(address(preimageOracleSingleton), hex"01"); @@ -132,7 +107,7 @@ contract DeployImplementationsOutput_Test is Test { vm.etch(address(l1StandardBridgeImpl), hex"01"); vm.etch(address(optimismMintableERC20FactoryImpl), hex"01"); vm.etch(address(disputeGameFactoryImpl), hex"01"); - dio.set(dio.opcmProxy.selector, address(opcmProxy)); + dio.set(dio.opcm.selector, address(opcm)); dio.set(dio.optimismPortalImpl.selector, address(optimismPortalImpl)); dio.set(dio.delayedWETHImpl.selector, address(delayedWETHImpl)); dio.set(dio.preimageOracleSingleton.selector, address(preimageOracleSingleton)); @@ -144,7 +119,7 @@ contract DeployImplementationsOutput_Test is Test { dio.set(dio.optimismMintableERC20FactoryImpl.selector, address(optimismMintableERC20FactoryImpl)); dio.set(dio.disputeGameFactoryImpl.selector, address(disputeGameFactoryImpl)); - assertEq(address(opcmProxy), address(dio.opcmProxy()), "50"); + assertEq(address(opcm), address(dio.opcm()), "50"); assertEq(address(optimismPortalImpl), address(dio.optimismPortalImpl()), "100"); assertEq(address(delayedWETHImpl), address(dio.delayedWETHImpl()), "200"); assertEq(address(preimageOracleSingleton), address(dio.preimageOracleSingleton()), "300"); @@ -273,7 +248,7 @@ contract DeployImplementations_Test is Test { function test_deployImplementation_succeeds() public { string memory deployContractsRelease = "dev-release"; - dii.set(dii.release.selector, deployContractsRelease); + dii.set(dii.l1ContractsRelease.selector, deployContractsRelease); deployImplementations.deploySystemConfigImpl(dii, dio); assertTrue(address(0) != address(dio.systemConfigImpl())); } @@ -282,7 +257,7 @@ contract DeployImplementations_Test is Test { // All hardcoded addresses below are taken from the superchain-registry config: // https://github.com/ethereum-optimism/superchain-registry/blob/be65d22f8128cf0c4e5b4e1f677daf86843426bf/validation/standard/standard-versions.toml#L11 string memory testRelease = "op-contracts/v1.6.0"; - dii.set(dii.release.selector, testRelease); + dii.set(dii.l1ContractsRelease.selector, testRelease); deployImplementations.deploySystemConfigImpl(dii, dio); address srSystemConfigImpl = address(0xF56D96B2535B932656d3c04Ebf51baBff241D886); @@ -335,71 +310,6 @@ contract DeployImplementations_Test is Test { assertEq(srDisputeGameFactoryImpl, address(dio.disputeGameFactoryImpl())); } - function test_deploy_atNonExistentRelease_reverts() public { - string memory unknownRelease = "op-contracts/v0.0.0"; - dii.set(dii.release.selector, unknownRelease); - - bytes memory expectedErr = - bytes(string.concat("DeployImplementations: failed to deploy release ", unknownRelease)); - - vm.expectRevert(expectedErr); - deployImplementations.deploySystemConfigImpl(dii, dio); - - vm.expectRevert(expectedErr); - deployImplementations.deployL1CrossDomainMessengerImpl(dii, dio); - - vm.expectRevert(expectedErr); - deployImplementations.deployL1ERC721BridgeImpl(dii, dio); - - vm.expectRevert(expectedErr); - deployImplementations.deployL1StandardBridgeImpl(dii, dio); - - vm.expectRevert(expectedErr); - deployImplementations.deployOptimismMintableERC20FactoryImpl(dii, dio); - - // TODO: Uncomment the code below when OPContractsManager is deployed based on release. Superchain-registry - // doesn't contain OPContractsManager yet. - // dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); - // dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); - // vm.etch(address(superchainConfigProxy), hex"01"); - // vm.etch(address(protocolVersionsProxy), hex"01"); - // vm.expectRevert(expectedErr); - // deployImplementations.deployOPContractsManagerImpl(dii, dio); - - dii.set(dii.proofMaturityDelaySeconds.selector, 1); - dii.set(dii.disputeGameFinalityDelaySeconds.selector, 2); - vm.expectRevert(expectedErr); - deployImplementations.deployOptimismPortalImpl(dii, dio); - - dii.set(dii.withdrawalDelaySeconds.selector, 1); - vm.expectRevert(expectedErr); - deployImplementations.deployDelayedWETHImpl(dii, dio); - - dii.set(dii.minProposalSizeBytes.selector, 1); - dii.set(dii.challengePeriodSeconds.selector, 2); - vm.expectRevert(expectedErr); - deployImplementations.deployPreimageOracleSingleton(dii, dio); - - address preImageOracleSingleton = makeAddr("preImageOracleSingleton"); - vm.etch(address(preImageOracleSingleton), hex"01"); - dio.set(dio.preimageOracleSingleton.selector, preImageOracleSingleton); - vm.expectRevert(expectedErr); - deployImplementations.deployMipsSingleton(dii, dio); - - vm.expectRevert(expectedErr); // fault proof contracts don't exist at this release - deployImplementations.deployDisputeGameFactoryImpl(dii, dio); - } - - function test_deploy_noContractExistsAtRelease_reverts() public { - string memory unknownRelease = "op-contracts/v1.3.0"; - dii.set(dii.release.selector, unknownRelease); - bytes memory expectedErr = - bytes(string.concat("DeployImplementations: failed to deploy release ", unknownRelease)); - - vm.expectRevert(expectedErr); // fault proof contracts don't exist at this release - deployImplementations.deployDisputeGameFactoryImpl(dii, dio); - } - function testFuzz_run_memory_succeeds(bytes32 _seed) public { withdrawalDelaySeconds = uint256(hash(_seed, 0)); minProposalSizeBytes = uint256(hash(_seed, 1)); @@ -409,7 +319,7 @@ contract DeployImplementations_Test is Test { string memory release = string(bytes.concat(hash(_seed, 5))); protocolVersionsProxy = IProtocolVersions(address(uint160(uint256(hash(_seed, 7))))); - // Must configure the ProxyAdmin contract which is used to upgrade the OPCM's proxy contract. + // Must configure the ProxyAdmin contract. IProxyAdmin superchainProxyAdmin = IProxyAdmin( DeployUtils.create1({ _name: "ProxyAdmin", @@ -439,10 +349,9 @@ contract DeployImplementations_Test is Test { dii.set(dii.proofMaturityDelaySeconds.selector, proofMaturityDelaySeconds); dii.set(dii.disputeGameFinalityDelaySeconds.selector, disputeGameFinalityDelaySeconds); dii.set(dii.mipsVersion.selector, 1); - dii.set(dii.release.selector, release); + dii.set(dii.l1ContractsRelease.selector, release); dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); - dii.set(dii.opcmProxyOwner.selector, msg.sender); deployImplementations.run(dii, dio); @@ -453,10 +362,9 @@ contract DeployImplementations_Test is Test { assertEq(proofMaturityDelaySeconds, dii.proofMaturityDelaySeconds(), "400"); assertEq(disputeGameFinalityDelaySeconds, dii.disputeGameFinalityDelaySeconds(), "500"); assertEq(1, dii.mipsVersion(), "512"); - assertEq(release, dii.release(), "525"); + assertEq(release, dii.l1ContractsRelease(), "525"); assertEq(address(superchainConfigProxy), address(dii.superchainConfigProxy()), "550"); assertEq(address(protocolVersionsProxy), address(dii.protocolVersionsProxy()), "575"); - assertEq(msg.sender, dii.opcmProxyOwner(), "580"); // Architecture assertions. assertEq(address(dio.mipsSingleton().oracle()), address(dio.preimageOracleSingleton()), "600"); @@ -475,7 +383,7 @@ contract DeployImplementations_Test is Test { dii.set(dii.disputeGameFinalityDelaySeconds.selector, disputeGameFinalityDelaySeconds); dii.set(dii.mipsVersion.selector, 1); string memory release = "dev-release"; - dii.set(dii.release.selector, release); + dii.set(dii.l1ContractsRelease.selector, release); dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 5249ded41cc9..5280328168b9 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -39,10 +39,10 @@ contract DeployOPChainInput_Test is Test { address unsafeBlockSigner = makeAddr("unsafeBlockSigner"); address proposer = makeAddr("proposer"); address challenger = makeAddr("challenger"); + address opcm = makeAddr("opcm"); uint32 basefeeScalar = 100; uint32 blobBaseFeeScalar = 200; uint256 l2ChainId = 300; - OPContractsManager opcm = OPContractsManager(makeAddr("opcm")); string saltMixer = "saltMixer"; function setUp() public { @@ -60,9 +60,8 @@ contract DeployOPChainInput_Test is Test { doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); doi.set(doi.allowCustomDisputeParameters.selector, true); - - (IProxy opcmProxy) = DeployUtils.buildERC1967ProxyWithImpl("opcmProxy"); - doi.set(doi.opcmProxy.selector, address(opcmProxy)); + doi.set(doi.opcm.selector, opcm); + vm.etch(opcm, hex"01"); // Compare the default inputs to the getter methods. assertEq(opChainProxyAdminOwner, doi.opChainProxyAdminOwner(), "200"); @@ -74,7 +73,7 @@ contract DeployOPChainInput_Test is Test { assertEq(basefeeScalar, doi.basefeeScalar(), "800"); assertEq(blobBaseFeeScalar, doi.blobBaseFeeScalar(), "900"); assertEq(l2ChainId, doi.l2ChainId(), "1000"); - assertEq(address(opcmProxy), address(doi.opcmProxy()), "1100"); + assertEq(opcm, address(doi.opcm()), "1100"); assertEq(true, doi.allowCustomDisputeParameters(), "1200"); } @@ -396,7 +395,7 @@ contract DeployOPChain_TestBase is Test { dii.set(dii.proofMaturityDelaySeconds.selector, proofMaturityDelaySeconds); dii.set(dii.disputeGameFinalityDelaySeconds.selector, disputeGameFinalityDelaySeconds); dii.set(dii.mipsVersion.selector, 1); - dii.set(dii.release.selector, release); + dii.set(dii.l1ContractsRelease.selector, release); dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); // End users of the DeployImplementations contract will need to set the `standardVersionsToml`. @@ -404,7 +403,7 @@ contract DeployOPChain_TestBase is Test { string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml"); string memory standardVersionsToml = vm.readFile(standardVersionsTomlPath); dii.set(dii.standardVersionsToml.selector, standardVersionsToml); - dii.set(dii.opcmProxyOwner.selector, address(1)); + deployImplementations.run(dii, dio); // Deploy DeployOpChain, but defer populating the input values to the test suites inheriting this contract. @@ -412,7 +411,7 @@ contract DeployOPChain_TestBase is Test { (doi, doo) = deployOPChain.etchIOContracts(); // Set the OPContractsManager input for DeployOPChain. - opcm = dio.opcmProxy(); + opcm = dio.opcm(); } // See the function of the same name in the `DeployImplementations_Test` contract of @@ -466,7 +465,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.basefeeScalar.selector, basefeeScalar); doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); - doi.set(doi.opcmProxy.selector, address(opcm)); // Not fuzzed since it must be an actual instance. + doi.set(doi.opcm.selector, address(opcm)); doi.set(doi.saltMixer.selector, saltMixer); doi.set(doi.gasLimit.selector, gasLimit); doi.set(doi.disputeGameType.selector, disputeGameType); @@ -559,7 +558,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.basefeeScalar.selector, basefeeScalar); doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); - doi.set(doi.opcmProxy.selector, address(opcm)); + doi.set(doi.opcm.selector, address(opcm)); doi.set(doi.saltMixer.selector, saltMixer); doi.set(doi.gasLimit.selector, gasLimit); doi.set(doi.disputeGameType.selector, disputeGameType); diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 3e5648fdfde0..49e35e835f36 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -16,6 +16,7 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol"; import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; +import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol"; import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; @@ -479,36 +480,37 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("gasLimit()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("eip1559Denominator()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("eip1559Elasticity()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfigInterop.initialize.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.initialize.selector }); - _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.minimumGasLimit.selector }); + _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfigInterop.minimumGasLimit.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("overhead()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("owner()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); - _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.resourceConfig.selector }); + _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfigInterop.resourceConfig.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("scalar()") }); _addSpec({ _name: "SystemConfigInterop", - _sel: ISystemConfig.setBatcherHash.selector, + _sel: ISystemConfigInterop.setBatcherHash.selector, _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", - _sel: ISystemConfig.setGasConfig.selector, + _sel: ISystemConfigInterop.setGasConfig.selector, _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", - _sel: ISystemConfig.setGasLimit.selector, + _sel: ISystemConfigInterop.setGasLimit.selector, _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", - _sel: ISystemConfig.setEIP1559Params.selector, + _sel: ISystemConfigInterop.setEIP1559Params.selector, _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ _name: "SystemConfigInterop", - _sel: ISystemConfig.setUnsafeBlockSigner.selector, + _sel: ISystemConfigInterop.setUnsafeBlockSigner.selector, _auth: Role.SYSTEMCONFIGOWNER }); _addSpec({ @@ -516,7 +518,7 @@ contract Specification_Test is CommonTest { _sel: _getSel("transferOwnership(address)"), _auth: Role.SYSTEMCONFIGOWNER }); - _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfig.unsafeBlockSigner.selector }); + _addSpec({ _name: "SystemConfigInterop", _sel: ISystemConfigInterop.unsafeBlockSigner.selector }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("version()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("l1CrossDomainMessenger()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("l1ERC721Bridge()") }); @@ -552,12 +554,6 @@ contract Specification_Test is CommonTest { _auth: Role.DEPENDENCYMANAGER }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("dependencyManager()") }); - _addSpec({ - _name: "SystemConfigInterop", - _sel: _getSel( - "initialize(address,uint32,uint32,bytes32,uint64,address,(uint32,uint8,uint8,uint32,uint32,uint128),address,(address,address,address,address,address,address,address),address)" - ) - }); // ProxyAdmin _addSpec({ _name: "ProxyAdmin", _sel: _getSel("addressManager()") }); @@ -841,27 +837,25 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OPContractsManager", _sel: _getSel("version()") }); _addSpec({ _name: "OPContractsManager", _sel: _getSel("superchainConfig()") }); _addSpec({ _name: "OPContractsManager", _sel: _getSel("protocolVersions()") }); - _addSpec({ _name: "OPContractsManager", _sel: _getSel("latestRelease()") }); - _addSpec({ _name: "OPContractsManager", _sel: _getSel("implementations(string,string)") }); + _addSpec({ _name: "OPContractsManager", _sel: _getSel("l1ContractsRelease()") }); _addSpec({ _name: "OPContractsManager", _sel: _getSel("systemConfigs(uint256)") }); _addSpec({ _name: "OPContractsManager", _sel: _getSel("OUTPUT_VERSION()") }); - _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.initialize.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.deploy.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.blueprints.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector }); + _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.implementations.selector }); // OPContractsManagerInterop _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("version()") }); _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("superchainConfig()") }); _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("protocolVersions()") }); - _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("latestRelease()") }); - _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("implementations(string,string)") }); + _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("l1ContractsRelease()") }); _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("systemConfigs(uint256)") }); _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("OUTPUT_VERSION()") }); - _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.initialize.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.deploy.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.blueprints.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector }); + _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.implementations.selector }); // DeputyGuardianModule _addSpec({ From 78fed1eee1fdff1e73673b514f9d4b382f25d503 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Sat, 16 Nov 2024 02:37:49 +0100 Subject: [PATCH 189/451] remove unnecessary check (#12914) * remove unnecessary check * fixes --- packages/contracts-bedrock/snapshots/semver-lock.json | 8 ++++---- packages/contracts-bedrock/src/L1/SystemConfig.sol | 5 ++--- packages/contracts-bedrock/src/L1/SystemConfigInterop.sol | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 8f40dca3ff43..dd2618cc5a7e 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -44,12 +44,12 @@ "sourceCodeHash": "0x39489a85bc3a5c8560f82d41b31bf7fe22f5b648f4ed538f61695a73092ea9eb" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0x429058f75d97fa7a7d0166b59830909bc722324feefc40f2b41419d6335d3f37", - "sourceCodeHash": "0x5ca776041a4ddc0d28ec55db7012d669481cd4601b0e71dbd3493a67b8a7e5a5" + "initCodeHash": "0x0eda38e2fb2687a324289f04ec8ad0d2afe51f45219d074740fb4a0e24ea6569", + "sourceCodeHash": "0x6dbbe8716ca8cd2fba3da8dcae0ca0c4b1f3e9dd04220fb24a15666b23300927" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x277a61dcabed81a15739a8e9ed50615252bcc687cebea852e00191d0a1fbe11f", - "sourceCodeHash": "0x38361a4f70a19e1b7819e933932a0c9fd2bcebaaebcbc7942f5c00dfaa2c28df" + "initCodeHash": "0x443fd84f8dbc38f03e59a56b99099b5e4b28de3e860a5d16c1a21101745622a4", + "sourceCodeHash": "0x5c2e00cd8939a538eb38580d76e70d27dd1e8e6cd9328e1978468981017736e6" }, "src/L2/BaseFeeVault.sol": { "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index afb9525403c7..517391164438 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -137,9 +137,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.3.0-beta.5 + /// @custom:semver 2.3.0-beta.6 function version() public pure virtual returns (string memory) { - return "2.3.0-beta.5"; + return "2.3.0-beta.6"; } /// @notice Constructs the SystemConfig contract. Cannot set @@ -224,7 +224,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { _setGasPayingToken(_addresses.gasPayingToken); _setResourceConfig(_config); - require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low"); } /// @notice Returns the minimum L2 gas limit that can be safely set for the system to diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 032109286596..9e9503fe6236 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -68,9 +68,9 @@ contract SystemConfigInterop is SystemConfig { Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.4 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); + return string.concat(super.version(), "+interop-beta.4"); } /// @notice Internal setter for the gas paying token address, includes validation. From 914157f97f1361cafa41e401b475d71ba84fc2e8 Mon Sep 17 00:00:00 2001 From: Inphi Date: Sun, 17 Nov 2024 06:49:52 -0800 Subject: [PATCH 190/451] devnet: Replace mt-cannon with 64-bit Cannon (#12924) --- Makefile | 4 +-- op-e2e/faultproofs/precompile_test.go | 34 +++++++++++++++---- op-program/README.md | 1 + .../scripts/deploy/Deploy.s.sol | 6 ++-- .../scripts/deploy/DeployDisputeGame.s.sol | 2 +- .../deploy/DeployImplementations.s.sol | 2 +- .../scripts/deploy/DeployMIPS.s.sol | 2 +- 7 files changed, 37 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 3f6f759c59fc..9830a3060cb2 100644 --- a/Makefile +++ b/Makefile @@ -147,8 +147,8 @@ cannon-prestate: op-program cannon ## Generates prestate using cannon and op-pro mv op-program/bin/0.json op-program/bin/prestate-proof.json .PHONY: cannon-prestate -cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded cannon format - ./cannon/bin/cannon load-elf --type multithreaded --path op-program/bin/op-program-client.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json +cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded64 cannon format + ./cannon/bin/cannon load-elf --type multithreaded64 --path op-program/bin/op-program-client64.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json ./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json --proof-fmt 'op-program/bin/%d-mt.json' --output "" mv op-program/bin/0-mt.json op-program/bin/prestate-proof-mt.json .PHONY: cannon-prestate-mt diff --git a/op-e2e/faultproofs/precompile_test.go b/op-e2e/faultproofs/precompile_test.go index 6d116bfa0993..2beabfba54d6 100644 --- a/op-e2e/faultproofs/precompile_test.go +++ b/op-e2e/faultproofs/precompile_test.go @@ -1,9 +1,12 @@ package faultproofs import ( + "bytes" "context" + "encoding/json" "math" "math/big" + "os/exec" "path/filepath" "testing" @@ -12,7 +15,6 @@ import ( "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" - "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -276,9 +278,29 @@ func runCannon(t *testing.T, ctx context.Context, sys *e2esys.System, inputs uti err := executor.DoGenerateProof(ctx, proofsDir, math.MaxUint, math.MaxUint, extraVmArgs...) require.NoError(t, err, "failed to generate proof") - state, err := versions.LoadStateFromFile(vm.FinalStatePath(proofsDir, cfg.Cannon.BinarySnapshots)) - require.NoError(t, err, "failed to parse state") - require.True(t, state.GetExited(), "cannon did not exit") - require.Zero(t, state.GetExitCode(), "cannon failed with exit code %d", state.GetExitCode()) - t.Logf("Completed in %d steps", state.GetStep()) + stdOut, _, err := runCmd(ctx, cfg.Cannon.VmBin, "witness", "--input", vm.FinalStatePath(proofsDir, cfg.Cannon.BinarySnapshots)) + require.NoError(t, err, "failed to run witness cmd") + type stateData struct { + Step uint64 `json:"step"` + ExitCode uint8 `json:"exitCode"` + Exited bool `json:"exited"` + } + var data stateData + err = json.Unmarshal([]byte(stdOut), &data) + require.NoError(t, err, "failed to parse state data") + require.True(t, data.Exited, "cannon did not exit") + require.Zero(t, data.ExitCode, "cannon failed with exit code %d", data.ExitCode) + t.Logf("Completed in %d steps", data.Step) +} + +func runCmd(ctx context.Context, binary string, args ...string) (stdOut string, stdErr string, err error) { + var outBuf bytes.Buffer + var errBuf bytes.Buffer + cmd := exec.CommandContext(ctx, binary, args...) + cmd.Stdout = &outBuf + cmd.Stderr = &errBuf + err = cmd.Run() + stdOut = outBuf.String() + stdErr = errBuf.String() + return } diff --git a/op-program/README.md b/op-program/README.md index 15e89ffb5cf7..932ec85db22f 100644 --- a/op-program/README.md +++ b/op-program/README.md @@ -45,6 +45,7 @@ After running `make reproducible-prestate`, the following files can be found in [./bin/](./bin/): - [`op-program`](./bin/op-program) - [`op-program-client.elf`](./bin/op-program-client.elf) +- [`op-program-client64.elf`](./bin/op-program-client64.elf) - [`prestate.bin.gz`](./bin/prestate.bin.gz) - [`prestate-proof.json`](./bin/prestate-proof.json) diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 0253e20a1956..26ef385c2f90 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -695,12 +695,12 @@ contract Deploy is Deployer { addr_ = address(oracle); } - /// @notice Deploy Mips VM. Deploys either MIPS or MIPS2 depending on the environment + /// @notice Deploy Mips VM. Deploys either MIPS or MIPS64 depending on the environment function deployMips() public broadcast returns (address addr_) { addr_ = DeployUtils.create2AndSave({ _save: this, _salt: _implSalt(), - _name: Config.useMultithreadedCannon() ? "MIPS2" : "MIPS", + _name: Config.useMultithreadedCannon() ? "MIPS64" : "MIPS", _args: DeployUtils.encodeConstructor( abi.encodeCall(IMIPS2.__constructor__, (IPreimageOracle(mustGetAddress("PreimageOracle")))) ) @@ -1019,7 +1019,7 @@ contract Deploy is Deployer { mipsAbsolutePrestate_ = Claim.wrap(abi.decode(bytes(Process.bash(string.concat("cat ", filePath, " | jq -r .pre"))), (bytes32))); console.log( - "[MT-Cannon Dispute Game] Using devnet MIPS2 Absolute prestate: %s", + "[MT-Cannon Dispute Game] Using devnet MIPS64 Absolute prestate: %s", vm.toString(Claim.unwrap(mipsAbsolutePrestate_)) ); } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol index 51b60c6c2995..c128260a21da 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol @@ -376,7 +376,7 @@ contract DeployDisputeGame is Script { vm.broadcast(msg.sender); singleton = IMIPS( DeployUtils.create1({ - _name: mipsVersion == 1 ? "MIPS" : "MIPS2", + _name: mipsVersion == 1 ? "MIPS" : "MIPS64", _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) }) ); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 9abff9149181..71ba435df4c3 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -802,7 +802,7 @@ contract DeployImplementations is Script { vm.broadcast(msg.sender); singleton = IMIPS( DeployUtils.create1({ - _name: mipsVersion == 1 ? "MIPS" : "MIPS2", + _name: mipsVersion == 1 ? "MIPS" : "MIPS64", _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) }) ); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol index 5890630c9c22..09ef6d059cd0 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol @@ -98,7 +98,7 @@ contract DeployMIPS is Script { vm.broadcast(msg.sender); singleton = IMIPS( DeployUtils.create1({ - _name: mipsVersion == 1 ? "MIPS" : "MIPS2", + _name: mipsVersion == 1 ? "MIPS" : "MIPS64", _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) }) ); From 7550853ecc343d6fb59f1d332390d9796cfb9ff1 Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Mon, 18 Nov 2024 14:59:38 +0800 Subject: [PATCH 191/451] Check `bcast.From` correctly (#12941) * ensure bcast.From == mgr.From() * address comment * fix for create2 --- op-deployer/pkg/deployer/broadcaster/keyed.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-deployer/pkg/deployer/broadcaster/keyed.go b/op-deployer/pkg/deployer/broadcaster/keyed.go index f5797d939156..c9bb27fcf0ce 100644 --- a/op-deployer/pkg/deployer/broadcaster/keyed.go +++ b/op-deployer/pkg/deployer/broadcaster/keyed.go @@ -90,6 +90,9 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) { } func (t *KeyedBroadcaster) Hook(bcast script.Broadcast) { + if bcast.Type != script.BroadcastCreate2 && bcast.From != t.mgr.From() { + panic(fmt.Sprintf("invalid from for broadcast:%v, expected:%v", bcast.From, t.mgr.From())) + } t.mtx.Lock() t.bcasts = append(t.bcasts, bcast) t.mtx.Unlock() From 873b3e0d6aece3f72dec734b0e2e26b3c962d548 Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 18 Nov 2024 09:19:32 +0000 Subject: [PATCH 192/451] op-batcher: fix channel duration timeout management (#12916) * op-batcher: fix channel duration timeout management Previously, we would use L1 data to help track channel durations. For example, the batcher would be configured to post data every hour. We update a global state variable with the latest l1 origin of a channel when it closed, and compute the deadline for that channel using a duration delta starting at that l1 origin timestamp. Since we changed the way autoDA switching works, a channel can be _closed_ (due to a duration timeout or other reason) and this will cause the l1 origin state variable to move forward, extending the deadline ready for the next channel. Crucially, with autoDA switching nowadays, the closed channel will not always be submitted on chain (it can be discarded and the blocks requeued). If it is discarded, the channel duration timeout has already been extended. The fix for this is to update the global state variable at channel submission time, not channel closing time. * add regression test for channel duration timeouts during requeue --- op-batcher/batcher/channel_manager.go | 27 +++++++++++----------- op-batcher/batcher/channel_manager_test.go | 16 +++++++++---- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 06403645ea4d..8e9870413a40 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -39,8 +39,9 @@ type channelManager struct { // All blocks since the last request for new tx data. blocks queue.Queue[*types.Block] - // The latest L1 block from all the L2 blocks in the most recently closed channel - l1OriginLastClosedChannel eth.BlockID + // The latest L1 block from all the L2 blocks in the most recently submitted channel. + // Used to track channel duration timeouts. + l1OriginLastSubmittedChannel eth.BlockID // The default ChannelConfig to use for the next channel defaultCfg ChannelConfig // last block hash - for reorg detection @@ -75,12 +76,12 @@ func (s *channelManager) SetChannelOutFactory(outFactory ChannelOutFactory) { // Clear clears the entire state of the channel manager. // It is intended to be used before launching op-batcher and after an L2 reorg. -func (s *channelManager) Clear(l1OriginLastClosedChannel eth.BlockID) { +func (s *channelManager) Clear(l1OriginLastSubmittedChannel eth.BlockID) { s.mu.Lock() defer s.mu.Unlock() s.log.Trace("clearing channel manager state") s.blocks.Clear() - s.l1OriginLastClosedChannel = l1OriginLastClosedChannel + s.l1OriginLastSubmittedChannel = l1OriginLastSubmittedChannel s.tip = common.Hash{} s.closed = false s.currentChannel = nil @@ -160,6 +161,12 @@ func (s *channelManager) nextTxData(channel *channel) (txData, error) { return txData{}, io.EOF // TODO: not enough data error instead } tx := channel.NextTxData() + + // update s.l1OriginLastSubmittedChannel so that the next + // channel's duration timeout will trigger properly + if channel.LatestL1Origin().Number > s.l1OriginLastSubmittedChannel.Number { + s.l1OriginLastSubmittedChannel = channel.LatestL1Origin() + } s.txChannels[tx.ID().String()] = channel return tx, nil } @@ -284,7 +291,7 @@ func (s *channelManager) ensureChannelWithSpace(l1Head eth.BlockID) error { return fmt.Errorf("creating channel out: %w", err) } - pc := newChannel(s.log, s.metr, cfg, s.rollupCfg, s.l1OriginLastClosedChannel.Number, channelOut) + pc := newChannel(s.log, s.metr, cfg, s.rollupCfg, s.l1OriginLastSubmittedChannel.Number, channelOut) s.currentChannel = pc s.channelQueue = append(s.channelQueue, pc) @@ -292,7 +299,7 @@ func (s *channelManager) ensureChannelWithSpace(l1Head eth.BlockID) error { s.log.Info("Created channel", "id", pc.ID(), "l1Head", l1Head, - "l1OriginLastClosedChannel", s.l1OriginLastClosedChannel, + "l1OriginLastSubmittedChannel", s.l1OriginLastSubmittedChannel, "blocks_pending", s.blocks.Len(), "batch_type", cfg.BatchType, "compression_algo", cfg.CompressorConfig.CompressionAlgo, @@ -374,11 +381,6 @@ func (s *channelManager) outputFrames() error { return nil } - lastClosedL1Origin := s.currentChannel.LatestL1Origin() - if lastClosedL1Origin.Number > s.l1OriginLastClosedChannel.Number { - s.l1OriginLastClosedChannel = lastClosedL1Origin - } - inBytes, outBytes := s.currentChannel.InputBytes(), s.currentChannel.OutputBytes() s.metr.RecordChannelClosed( s.currentChannel.ID(), @@ -401,12 +403,11 @@ func (s *channelManager) outputFrames() error { "input_bytes", inBytes, "output_bytes", outBytes, "oldest_l1_origin", s.currentChannel.OldestL1Origin(), - "l1_origin", lastClosedL1Origin, + "l1_origin", s.currentChannel.LatestL1Origin(), "oldest_l2", s.currentChannel.OldestL2(), "latest_l2", s.currentChannel.LatestL2(), "full_reason", s.currentChannel.FullErr(), "compr_ratio", comprRatio, - "latest_l1_origin", s.l1OriginLastClosedChannel, ) return nil } diff --git a/op-batcher/batcher/channel_manager_test.go b/op-batcher/batcher/channel_manager_test.go index 8dcb0745c164..d3a67a1bf1f4 100644 --- a/op-batcher/batcher/channel_manager_test.go +++ b/op-batcher/batcher/channel_manager_test.go @@ -130,7 +130,7 @@ func ChannelManager_Clear(t *testing.T, batchType uint) { // Channel Manager state should be empty by default require.Empty(m.blocks) - require.Equal(eth.BlockID{}, m.l1OriginLastClosedChannel) + require.Equal(eth.BlockID{}, m.l1OriginLastSubmittedChannel) require.Equal(common.Hash{}, m.tip) require.Nil(m.currentChannel) require.Empty(m.channelQueue) @@ -161,8 +161,8 @@ func ChannelManager_Clear(t *testing.T, batchType uint) { require.NoError(m.outputFrames()) _, err := m.nextTxData(m.currentChannel) require.NoError(err) - require.NotNil(m.l1OriginLastClosedChannel) require.Len(m.blocks, 0) + require.NotNil(m.l1OriginLastSubmittedChannel) require.Equal(newL1Tip, m.tip) require.Len(m.currentChannel.pendingTransactions, 1) @@ -184,7 +184,7 @@ func ChannelManager_Clear(t *testing.T, batchType uint) { // Check that the entire channel manager state cleared require.Empty(m.blocks) - require.Equal(uint64(123), m.l1OriginLastClosedChannel.Number) + require.Equal(uint64(123), m.l1OriginLastSubmittedChannel.Number) require.Equal(common.Hash{}, m.tip) require.Nil(m.currentChannel) require.Empty(m.channelQueue) @@ -475,7 +475,7 @@ func TestChannelManager_ChannelCreation(t *testing.T) { t.Run(test.name, func(t *testing.T) { m := NewChannelManager(l, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - m.l1OriginLastClosedChannel = test.safeL1Block + m.l1OriginLastSubmittedChannel = test.safeL1Block require.Nil(t, m.currentChannel) require.NoError(t, m.ensureChannelWithSpace(eth.BlockID{})) @@ -639,6 +639,8 @@ func TestChannelManager_Requeue(t *testing.T) { // Assert that at least one block was processed into the channel require.NotContains(t, m.blocks, blockA) + l1OriginBeforeRequeue := m.l1OriginLastSubmittedChannel + // Call the function we are testing m.Requeue(m.defaultCfg) @@ -646,6 +648,12 @@ func TestChannelManager_Requeue(t *testing.T) { require.Equal(t, m.blocks, stateSnapshot) require.Empty(t, m.channelQueue) + // Ensure the l1OridingLastSubmittedChannel was + // not changed. This ensures the next channel + // has its duration timeout deadline computed + // properly. + require.Equal(t, l1OriginBeforeRequeue, m.l1OriginLastSubmittedChannel) + // Trigger the blocks -> channelQueue data pipelining again require.NoError(t, m.ensureChannelWithSpace(eth.BlockID{})) require.NotEmpty(t, m.channelQueue) From c91fe2f68791f7242c8768d4d08ead128a23a2c8 Mon Sep 17 00:00:00 2001 From: George Knee Date: Mon, 18 Nov 2024 09:46:24 +0000 Subject: [PATCH 193/451] batcher: keep blocks, channels and frames in strict order & simplify reorg handling (#12390) * use a queue.Queue for channelBuilder.frames * remove pop and push terminology * proliferate queue.Queue type * simplify requeue method * undo changes to submodule * sketch out new arch https://www.notion.so/oplabs/op-batcher-re-architecture-114f153ee162803d943ff4628ab6578f * add TODO * add channelManager.pruneSafeBlocks method and integrate into main loop * fix frameCursor semantics * fixup tests * avoid Rewind() in tests * only rewind cursor in rewind (never move it forward) * fix assertions * prune channels whose blocks are now safe * handle case when rewinding a channel with no blocks this is strange, I don't think we should expect channels with frames but no blocks... * add clarification * implement channelManager.pendinBlocks() method * fix pruning logic * simplify pruneChannels * simplify pruneSafeBlocks * add unit tests for pruneSafeBlocks * fix pruneSafeBlocks to avoid underflow * improve test * add unit tests for pruneChannels * introduce handleChannelTimeout and simplify channel.TxConfirmed API * factor out channelManager.rewindToBlockWithHash * change test expectation * do more pruning in test * Replace "clean shutdown" behaviour with waitNodeSync() Instead of optimizing for a clean shutdown (which we couldn't guarantee anyway), this change optimizes for code simplicity. This change also helps us restrict the amount of code which mutates the channelQueue (removePendingChannel was doing removal of channels at arbitrary positions in the queue). The downside is that we may end up needlessly resubmitting some data after the reset. Reorgs are rare, so it makes sense to optimize for correctness rather than DA costs here. * Add readme and architecture diagram * don't panic when there is a safe chain reorg * fix test * readability improvements * only clear state after waiting for node to sync * resize image * tweak readme * typo * rewindToBlockWithHash never moves cursor forward * use s.pendingBlocks() * add log line * check there are blocks when handling timeout * rename HasFrame() to HasPendingFrame() * fixup test * improve readme * link to open issues by tag * add log when main loop returns * pass blockID to rewindToBlock and panic if block does not exist * don't remove all channels when a channel times out keep older channels, it's possible that they also time out * use newSafeHead.L1Origin in Clear() when pruning blocks * clarify comment * use warn log level on safe chain reorg pruning, and unify handling for safe head above unsafe head * update panic message * extend test coverage and fix bug * rename test blocks * simplify HasPendingFrame() method * simplify implementation of RewindFrameCursor * activate dormant test * ensure pending_blocks_bytes_current metric is tracked properly * cover metrics behaviour in test using new TestMetrics struct * extend test coverage to channelManager.handleChannelTimeout * add comment to TxFailed * rename test fn * point to e2e tests in readme. * readme: performance -> throughput * improve channel_manager_test to assert old channels are not affected by requeue or timeout * fix handleChannelTimeout behaviour We were trimming older channels and keeping new ones. We need to trim newer channels and keep old ones. Fixes associated test (see previous commit). * tighten up requirements for invalidating a channel * replace requeue with handleChannelInvalidated --- op-batcher/architecture.png | Bin 0 -> 286852 bytes op-batcher/batcher/channel.go | 37 +- op-batcher/batcher/channel_builder.go | 61 +-- op-batcher/batcher/channel_builder_test.go | 21 +- op-batcher/batcher/channel_manager.go | 235 +++++------ op-batcher/batcher/channel_manager_test.go | 435 +++++++++------------ op-batcher/batcher/channel_test.go | 15 +- op-batcher/batcher/driver.go | 90 ++--- op-batcher/metrics/test.go | 22 ++ op-batcher/readme.md | 69 ++++ 10 files changed, 503 insertions(+), 482 deletions(-) create mode 100644 op-batcher/architecture.png create mode 100644 op-batcher/metrics/test.go create mode 100644 op-batcher/readme.md diff --git a/op-batcher/architecture.png b/op-batcher/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..0eab940fbb5224f4a33ce70cc9ec27732e2d813f GIT binary patch literal 286852 zcmeFYWmsIxvM@YIkYsR3a1R<_fFwAB1=oO;0*5W&cHXx z-e>RqocDb9-uK7*@6J3kt503|8V0r)z{~gsUyi}Plcqn zzFr#9&?1NgQDML9@CSys1X5EY_63H&#V372X@Kxz8U^9W1v&>q-fD*rMy!+fn}o#b z1-IS(`XOGO`@M=wL8sK{A4q`2K3p^oT^9hyHBTEFSV7~1J0b=bN+19gt%XRfe0l~^ zTulvI$lUV2zOfl8WBWSi+P(<#aNnGS7?3=O0(dOS9TYt{BzA?&L2K>}LIsQ?h)roG z(j;v3qbYxu^hN`xY{aO?$=Zzpvqm`um2kAt3ISg9Wom8s0Lhy``F_J(N*l=qG|gik ze!;dL(t3RXfap~8s*rT)A)r;^MhYt+9juslsg;C7TAFg@t zs|}3;H*5*1ePfu;B7}^xS+^{2f`u4QqeVnE6S@V}%9_wYO|4vgH8e(K7;k?=fam{~d^ZYTdk&Pz}2Pw)0fH&66M zHn{l&III`CWeT3W!5O=$bq5b$H#Chk8XG+rHuLA$TgZQkX|_Ksx`sD(xex4dn_7?- zR}VKRRF3OJt}+fj??H>cvE#hUc6GGOwzt5vrQDqIms->#W~MX`_vSi65bhC08f|$x zO&P(0hAPMdegD%K-}yUs(bJQ+Z|zKgYjHNFvVwE^RD)f8U6@|me0*-S3k#{WA(0%O z+RR%hx}!$Ow5J+9%7E~6;czS`ZO&3;ASo?8qi@G)W z2Q;hCg-fniASjAc`_$50DWNBVLIyw@t6CX9|8TGkKik|5~Cx12#Hp@|yQaBc@hxu{c-8+{m*LuF-ZA^cxcY(9xCz zy%cs?SyH-36}ZZN`5mMkDIFg=pk1N*3kGFshDA7&IJ*&H-8p(wx-w;MrQfGn_W+K_ zeBSNL?`({?RX;F|r%gYH!S`v}pxtn_{D)Mq_Qh zpvN!wnn&h;;bF2fj#~a&>DpCWt~KpGyfuvV#x?bm-8Hi{ zgZBAP{tK}3<3{)p5V{Hw*RHd%gu=K2-Qjw zebdhue@FK!OhZIKWIszf20wN$Mn6V{ae^UL370u51|v4HkEqYEuRG?D0ivBz>s97e zuiNhv_sI8&U|wFKbDs74;P)EuEeqXMzUANKSxif1;|HU!}UAIVKxhM%oFy3M~5}gsdgI;okzJi)hTXGv^>vz2DN}crYL?Y|KkkeStzz{YT6VmDWIA{`cst~tlaMHrRLO-6vS^ln zFWGmU!H1-bQTo~3+0C+& zVyj@Qijt1u1*i<*q;%v&6m(x+-{mE* zC9+m5QJ}~b)&5t?7@|6wGo=J2HI-?$<>{l+qoku;_;$O-X^e{LS&x#7I?UqF>dPb- zBasNnbkkqckL!5Zrp#PHQ}COsiwPJ z?MsO5R%I+&DXD9kwLg!W7(Q`7_0;gyZrA=9T@u~Q4sm8N*l%(esmpc{*gcU{LOXo?^g7Qyr5>UrulU#-{pnh%pgK8E?N`pLXf7_;ik? zzS^C617C-wj4A)c_1iwy#v9*rtoLA93%P|^)eXfeClW^z$Xx3F!5m2+LEmYggd_5y zNnyr&eAR}zTKCOZ>H5Aptq!eBt(Ag|g7Ep9`s)4rx2YN)^<5P?KUA_DW%dIEQUrY6 z1TLz^O($c=J?^6}SLZ!EZ)bNfc9|~?E;kP#u9O=>hYDxu#8?GnFL+>A>QIy)-z}pnbE`u5R`^ilAEoSU+i&wGz+JN-HoQ*u5O}x=&{77Y1ll6?_vEW z%gQG|`;2FYiM!3a29qHr{?f4EUah)eR3XO~& zg*O)?eDzKOczSpdi^fgqcZzpP=LYTcH@OSLW$Ed1#dkipZ_g*z%r>iAtgv!9a&wSh zP?_Zr2vH%)%cCp}QUQ970g5*Wl&op{`NqH`7zIdl34Cn6C}4mIAUy_9nj--Owa^qU zDSIN{a05rP_yOi-%w%@3L)j8Ri<1C^qk-ybTOe-p9KyY6g~y%F&iy7ZTj}BT!xQOJ zR4wYIODwqc8fT~`Wh5sDV1k#?0FMxG0Z8x?0{ja=AO<}8T?PQ85lH?jzeQm9vkxKw z5MT;G{Al1sT>)LUA3CE|?fJ2iVXIayuAI ztj-qJzs3RZJM+Se7KZkEl+G6BmUg_(0@S~I@WRW#s@bS1e|NDr6QEX;Q=}BNvNfc9 z%?e@#Q42n%q@?7x{bdjUeB4u(vIejLH;$4n4z74t*N!Wsg)(=uW|MCtsLwHsHuNV^w;MPJPn;q z|DMUx?$5`9KOoz$7B+TP5ZiwOvo|&R{{j2e@(0-O=lWwh{$GRfDw;YQnyZPKTEIyS zM@^8E=hbWe-}C$*O@Cwh2k3h{Lt9ZR3pk{`;NOe&C-6TT{~hr6oa%qi$m63&O$vQ}w??ss9Zo$o}%>pFsbt{dWkp{|NEV+JA>o zur-AfLhl!9g6x0J@XxwG`}4E?TKs=7!ylaXyB5xBf{*#x{$g9f#}6(83;=)-KuS#b zoioB-Dr!o~`^M%)Axb}cTn!O*S3G=Su`;j6jxU}O6fsvBfX62ow2}yb(D@##c58L@K4$U|jJHo&4|MckLy*YW0q2!-Un<-9X1@8-AKP5(& z6hg#}^g^bkMEI|YEy(nxy~qm9z48BT*FQSYB0k<~Li-<#{Hwe7V}wWcI!ON&0{@s+ z=(87MU5WR9hw`5g86rXm^Hcw$hlE2YQe}7@nEltZ_#2fxY(=5VSN}cr{(yLg#*lGK z{6C|&7cvtv{)h+0f3qxqp+fMB+$jH#$ldah5svk|uknYY$xY+<7 z`lha7fi(B}eZwoBS2&K`;vZHBFS+$?n@@Ikm{t3y0!3|OVCWDUOg**(yFq7h{^1l? zroTnPNGqHj`MB*ulaMJ}0BuRH%z_aZVj2pIKGS7s;Mc;9(*zI$swojh3Kda*N4!sxjM+eo(3mQCgTL9+;plCHj&hHuWTmxwXl z=R47^$DyvgJ6~NLQR=1+TytWtD*5)j$7Z(FnQcruEwhgLE+yKAZ3(kn2J86@`(pG% zSgJ}{*prqpWDrFuTOOvoZyM*-1YePC8tr2sm?_JPbKFIp)#rE2TbUNEw7SWWbsRR` z9+*Avn~nX84{-?%5TluV^?sN(7?2YA!X?}duI`b`^~ri?;Y`UO8P>_d3=ppl1 z#1)C}#p%YNj1OdR!l%OT#Prkjzc(aE zA30AUjzx2lFY)`_PBacuR=1Y0S5-$}EOUq)*Zj*L&!MAv%;kfU@y3>;S!o54!kpJNeZ8w)U*mpoVtyoaiYDDyQmOmsjC+~ z-H4A;UztsE%YKzgVnB|2q5g#~n}ce&sza>T;pYqsa^_;cSiD8mOpa!z1^@FAT<_0M zp1r55wo(~L;o+2XG#(2S|67k_RG^>1@@$Q}C#I!z+pse@JNwL3(gK`^m_9t!R)vJA z>Yln&;td@hSkkqs;)^4mD zb~?7bOjE7C3&5F+EjJTB@|6oSJJ))wt(WT>TL1pyN8t{UBXQy<&xy8H?KQoc-ubCh zrsijkiD+Kx^^V^c!=6NvKZgtUP!Iw#h9iPrzf#BPCo-IKSJJGM5^=gcv1_fy1B%g; zQ`;Gx?|e5?qRToux94{y^M!T!6K=J0N#7+UEK5`tKPP``$P`d9@B-`6--YgScfktg z`BUzsUJaf@W9B;@4v3w?pc@w}4d&H5O>qaM&$>05I#XxM_Eh(!VBE3$s_jEs=U^-@ z-%gqZJa@GEK-B$_dZxt%lTec2;Fjj zhk!VJYO9{w`@!r5{!n+cA>jgdff1OvqYd7D!0l|KhUex1$y~{e*L;t0ldpW;6&PoG zp#Av6Z;qRlpzPDGYEFA?p;6MU)X?-%m_3reCGtrr*u*8&M7)C-eKBA4v|{l* zhNSZ;k*{rDHzpM}><#q$7lgT5o><#LPD=YO3x_}5cb}H)tRJn^nX^Gv_0#3swO^&o z{&bx>u1rh5)1I)n(g|^!&L_VG54Z1`Ra-TU?@io=pNWyK65hv@+%A?}$AbHZ+p)s0 zVO^oOv;Fg2Cs`X-EP}Q5JEz3;CaPui86LAKF`IuG1n?bMgZRgq=_0z@#Z2fB^Hxp_ z(ep3)1i78$W)KOEsb`~ew!Y;XK0`$K9>_C#+Ez-aW;F=FG6nU zBjJ@SquMz)bl%I@fl_7eDy_Um+_VmJ+sG@h4bDeYIZm!AVd;PIOQeue4dQ&Y5F(kh zm%Iv)X*@)~(Rh@-&=}GHO-3^%?QJNyT)@_@uknA_KVef_vZ{vxn+1Mpg~%2mWMT^i zjYgdk^YA)=4yqeDRY0RP$<%SfjKPG5#bsc z&yF!5tDYaaPfu{%d}kQzgLc2xJ-LKS0PoS~*y|!Z#0x3h2~v#QgH+0r^afMBz>`TI@$ZSS=rbaQ$0{>RS}Tf!?X8OY)t!<>aZ*@0cN zKk+8CM10vr=O5Cq9&YG4sd^G=qSGSU5A8VY5zjwMM)nbEy!1ETcXcAZrdY;0=YD&S z`AV=3E`n)6qv$W0;*;|gc4{%4&eMLh)xE#`K1_Png zRh^E!LB&PiK?B^7s*4cHdN@=vekrYVzzVkd*gSI^cK691LJ0|2L3yxsx^^L-)OEj^ z-QP97H_zJ)TX}{5KzuzRO-%AR`8GA3s~kK6Tdt$%u}YWv+dvM*@RnOVFLr9QGWU7? z4qY+>2Z2||9VwM&(X2x(;014Rp~`or`{M`*e)G*lNibY#;M;8sFy_c6ei>{lz9zS< z^}Q`$c7bBmUpu4()R(8$46~=%EELuAyF>MCAq7ssmHTw2pQUCFOF2g)tTh{59rW?% zYBptyYgU3+h9UQMt;cW+fpwb0X0k~AjX8b%+&ce>x5cvFkOMS?Kal2!j@!u9pQ0g^;rL2h`?g@yfqNLrV{?ZoS#ghbEbRVpYTRiGQ!)(?eKn~H>SVb_L(sa zwr@~4YZ-iWw2KP95!}th7ZrEe-J#mc#( zDnXStBNr!^zrKeX6dzI`_b!YNz4RwZpogn&@YA*B*K_7S*;tk-j)RA<*Dg;$)bs0Z z4Z}57}Zme*nh>6fgGdE$?dfnV$GMf3$STS510y}SndCxu=?D*Xqf*i(Q zO?^;Sw1h2VK_nfp3}s?|D9J14*vLB@;yIq&#w?yW<;`W5{BXZMdDo>JCzt&Cd%8|7 z*bmRNoo%Nr)p0hT3)AJLty2dT6n1~v%+_?8dWetZ+=3Jw!q&t+;Z_Z`m9gn2bIQ&e zs}6bD$*v&ki3R4G`S`Z%WAB$tj@N2?b-Tr#&{{jZTr>*41gO?k{M=Xbjif~^$W{9a z)NWYroEA2)q&=(!4KWy0XnPS%V%5ODP6eAvwya~EKv2$=m$L|1gdI3_K5KSXv03s) zeF`mZ>b4QJ=Q;l7(N#3UG3TN_5YC0P-y)K98Zf#Gtp<0U6)|zJ$2UDjd3j%ke zmFX=Jkh|5Dd+|)-&mwpYilSiF^*LEp8R&Y`zd|8yKO-T`OQcGT`T@qgLePh;BJ8f8!5H0 z4vBeei95zAvT;$uHqSP^v`Ccc+VGnoJJ~B7iGx&+iwFG86g$jZk3H4)yxLxventu} z+nC=Kwx{ij;;W15=2T>?l}q?l0R4%qj~I8G-@~%tWyGm%(khQ74Y>_lc-TQe zaw(i7cAi~C#An)L_kt01Z;eb-&J20RcXGa-Ey8_+Tx^nQs{u<&OIT zogvz1nY49&$$_e*G^|#hr^!#m@D8S)@Il>T;PNxQE@H~e_@9;1Z#S=}e4Y8>qj_Ue9^?JRY;>|?(BtJRSDgSsbX zjB+GgmXhbFQ>_Bffcl!Dh~uI!pHgzr@fAEzqC*~Tn;!}T*L7Z8%$k*~2pqa7B&`MT z(Je-4%kreypWcIA2&0+`i|d%evOwNmZiXneBn?3|ov2^VQ8C@Jc_m5%$fFIUqrm*L`2 zbFX4pFu&pn5APM^c5d;p3L58W;?M>X#Z^n?PC(+>0IyE9r515Qwq2jOIf~P92nFYS z;(NJVoa3~_1$$DWE2E9eaK3|tH1_E}O2mpCH&@rU{Q8eb$i|x=V28crNSgzb#$9*o z@j7XH<>hIKi`k#$ne0P-@}bE z+}<7!T6uC#>;f$xZl;>$E&TYQYGqC*y}9@iE(187kcUf1Zrz64;;pC6N|2STN;{?n z{w(e|WBp;1+j_OcxA1&NoxI$<^rvjv(7AgwC*Vf|XZgm{9tpwn1VIm1-rVo(RtpXK z53oxG2kc9!C;K!iLO7XGzJ(YN&>7c(Ra zNB-|hlmoz9!dOgc_h-4e7evQe)s`OL2Av#i_J2&}yk2G{KuB?Qcel-vg2PP=}{&{4LxDKayZ^r zfbiNRvm1y9(-%&f&arCMz5Jo&%Cs*Z2DhvP(MFtFLEFJW%~tNKBtIW+A8tw>n9@#D zHnYp)PXbS(1rJ&4_cXRw-#OW2MfiMu5P?L`D=}CxDlmWD=KWzm4L*sA$d)J9 z%pS}CIDxmJ0J%}dl~Q+cUkRem@wW1FM!_QdV1!NROEFNC8y%w8;v;N(X8`cQnxrn2?eOKN{?*AUq9*Sc^L!ua6~uK4}lh?wpXI6j;2}N zqZNQvh~Z*|O>O;9H`Q2t1$j7p_||HNQUEG)?N(4pFMCYRCp)Q|tg`?{nH?6mH117b zYxz@|mLZ#_CU)!Rx~o3S zOE=*gxOnK?%{O+KMFBIx?ZROI9eNzPDUI38?Iz5}ajpFoY`u)r6&SIoeVP}z^RueG3{)DGH08J!Yo-z*YAu-DXiBEGu6*)l}LS*KlpKKFe!ygV*w zH#gnWRk*g#FBfj52%=Yk0$rn;h}yVUcx}9e`Ln35&m-1U+M#1mrLL0J!P=8tz& zn|V7EPhd(BuI~!m2$6)2=;iiseOn+$t*GBD4spg9AyRmxGs2&xCeKI`&&1|MQ)O$1 zkWJ}^j$Lqf2xgH8)7bUAZUVK`5)}PO#2m&%9@6I#!?{-i`)b-Cxz^NjH4#p_(Q*aR zt9iR1*^^wWn?MPE>M&zPD@7eewu(*<1w#&k`dHn(#Xuw%1c+lB73Qer5?Wzagdp#= zTNhEfoXb^Qb;CFAgPPSZI%sDJB_8``+ghv9?15(uzH5(MPT;yqFf4!V`U~l`#6MhG zW_0th*s|ASxCu+hTeRh`=1$Wt8!E>F!iiHwP*Aoqn&4b0qtAo*>SF7R{WQhOm0dHf zRgyQ>{sfzUJA*?KJN2POhCZcLgEtW^XW&!rY6ykB^Y$8Ee2M#dq14AtGBOzyR6=Ar zAWDIaOaKX0oG=2475H{$IOhtaCLEBKxI&0ROt@xLiV*FT_7md)7y|G`0-A$_knO7O ze0dHLd}o1zoPf?2J`@BW?{7|U*TPiskvE`yiC?qiL=fcVqlfJl!ctJk;XI{A=A}E- z@`(BQ)c_!xOO2_0v4R{|gwZ&TAWwO;Ev2U&v}f3>t4X%pD$>f6BamN{h@Z)S=Yl z%4~{Y_SO(L!W}$6Q)J`t7^|4kv(7}yGuR;nEN7V;PR#-}F2~K})1$6i^&d6?mED=? z+%B$J-BAhb9d-I4^hrZ;F0m78=`7BEVs|XJbg=FS-sp^5jOUJ{k3M9ki_ecJ<0P|N zhukE%UcsGd$#q^sR!jXt*B!bY-z$7nyd9kEe6t>s6`z*yp1}@Sb<_EGQ**E0C!rW1 zr59b~TMBLduCTM8;OHI472NF=(hBQk7Sl`w2TK2W73#$v8I>OflFaTwc&pE3Woepw z6n1zCj}8=7-;l|w$yQ|zrM%4Fc^2KBN0DXOje)ne1yYIMD{}|{%7fppd z&c6OKJNZ89@3V?6V#Ep41}!`-8Yb@`IjPMP`hI<$3Lt+4}mqF!N?Lt(ST zv#h_xZ=m+06N}590cUK+k(IZ|PmZ2MRDz`4p@U}rNI??~bkr973Wbl5@_J_2GY+L+ zozZKnRfreq1XyaIzZB&Cl@N`Rg1tf(VSwx5Fd*HYE~z0$ldZbdtl_dj=o#0jWWBcOs>oOV6kup^W;x_oD(- zFoijyqj;~tEp6?B`UJ(U7T!V(Db*rYgCFGjY!(%1w~qqz=%eXdd$ExgB^y?kin zvMX(h;uVEx{`(v3XZ4_Q&9f-RQ-)p8IM-&{?a#{Fr|0;F)D9$uFRAUu-Fj`;vDO(4 zoy^g7SXMuM{*X;iSI4Oye3H17rF$uTgcB*44BriqPvX- z5)yA3$M;9mG7e>$B*0H|2&>X_wSc@E83_N7ojIQEbb z6`4B9(^XBY0Y4+`=x{1BRo;7`q<@WEZrsqT!W3>}z$@On0l#ia>wuoYV7e+uyTE;E zT|g^z>*a!z&U-+v0cl}v8%4PNYoZq|7!~{KU*#JJmolQskAsKY?9J)bo<=y^_L`wA z7>pz*-V`0J&g)_Ox$8YcQo%2+qXr)6V^f~+Bcq@YTle$%O}79LuL)fen(<`sgy11J zpUVgW6ZQ;>HX*-lrQiCOHy&WfkF>4Cr8sGj#|GjrDNZQHIOz5WM?)HiJ8`L9x-SdU z^JPHnBNX~f`>4RVd0bZJ_IxgrT#%ZaGtTS6?Sv0K(wr*H{%Wn+EI_2W$}Ew+LDXHG z9wJb#md}y58_EujbY8n8eX|J#jzU|PUmnSeu|Zx)o98{lEI2!;E+N(-aJh_$4BW1j zjmK4aXpN*m$N086&9;wGDx+qvyq~$SGD1cKjrlfjQBM|{-;d)Dx(Z=H3Mf#lEK?nb z0P%?5hF_3eW4rk3w%DnOLAm{5Xh;ZYRxLE#RIJA|tY{##MOo68bX}5t$r=u+m>|#A z*9&}Y{7vOZfO?uTAM)m2hzg%d!x-Gl){>{}qolJ_&Q%nYn0eETB%0_K)GITcMH*D@ zcY+CNE5R1dV)LD2(9EOH~THC=HIvkgO$RUv5tf8 zUtcidam|^Zz9avcu7A$&VcF-{xAuuFsX-oMtpMS_qm}RAYooOsM><<&Fk@7f!g6?~ znY$W(^X2}TArswBny#pe%*!F@slO z=~1XuAiMNCgZhg=u1nD9v(TD%hm-f^1?jJmUeGkRS&s7*A=6{5U1z>|5}hGO)+q+q zAtsD=k|j5$MzlZx-XDdyhB!+QUe2q|OMQ03F?IxqjnHddVbB8`-~kd>dWL^f6B>w? z%L_l$jXUV+ygzlvgcE6|m$wC4#iYU`gy^VbeOnFe1}WjRzD?!^T>vECkbx^B?1cR_ z!h~-IlAGj8(Xri71DV%A--%@%`|`x=hwEX8po2A$ePHWPX$a>aAQw-nI}P)><`Pvn zN1zmHEGBOzn7NTKp-X@{My(c~Y<#OD7wsx{ADK{5*UUp@ylkKH5F15Fpd;ve)hyG# zd5Z^%9SHcYv@h&3*ePfR27G&}UFt!m&*u{Hz)pndw8k!S4UbwUqjb;k#yg?3rdq7o zL}OL0iqrf~N=Q}kI=K%3-|5Y2rzn(^793AOwANkqj* zIk!|JrPZ_J5wuI%psMZLf!?kaFq4^iIpON_)=<@MU{jG;sJE+%U&R|{--k*kwd1Z$ z18nT+;<$bD{OY`P6Q_~l$G$B>0tClmJvpo&`D}p~`D|JD`h3?Pyt*-cPtRq?os_Ee z>1PFwI&@K&{a%ydJu=zsgh4Sc(VpP|Mgu2qD=&2kEvmyvxUww_kDusy)##(8PwW;w zUW;a~oJdgYjq41=3|tKyDcTFEZ1t-`^q)tW#4rs_AYrELnd(jeCJ393IPzrUq>;{KDAU30eth#Imn-A`s>$+nH z+C~UIS|^G2-jaEX(&?vLwVjieA*)Zo>7leIF(W@CM)ZcK-O?Owah%9~ibT|f!r_?* z%?;OYKak4!d(IVNZi@a$M0ks}5>4Xpx$ehWESG7g43O;|nPLZD0f|2=R zE=QSyqc(*%(kiT*t2%l!7Cdf~9g!*}Nnw*Sl&KJq9ur#>+O4=qbyi`)17qv^*e83V zBa0b$!w|0YtTQULYkK0dAk8XrLf+go?9s#3M^pzS&kjtcY~~tHgitBKIK6B}b7?u6 z7jI%sd7D7T4jN~;(t}FFJ{~LHl?!T5NGZuVXiH5EnZI2+7>|ejK<;8g;Qeer#M3q4I&`f^cqRZp*ZHZ-N~Xv=QWVL1 zcSQ3x7gsOZVs>ksqerGYtyLKXpw`�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%0AeJ6N0={5)t?k6^;lB1XM|6*o-0L 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�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 GZ-$0M0#bqMmV5^PEH-3C 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 literal 0 HcmV?d00001 diff --git a/op-batcher/batcher/channel.go b/op-batcher/batcher/channel.go index dd0827d4686c..9ae52be405dc 100644 --- a/op-batcher/batcher/channel.go +++ b/op-batcher/batcher/channel.go @@ -49,10 +49,10 @@ func newChannel(log log.Logger, metr metrics.Metricer, cfg ChannelConfig, rollup func (c *channel) TxFailed(id string) { if data, ok := c.pendingTransactions[id]; ok { c.log.Trace("marked transaction as failed", "id", id) - // Note: when the batcher is changed to send multiple frames per tx, - // this needs to be changed to iterate over all frames of the tx data - // and re-queue them. - c.channelBuilder.PushFrames(data.Frames()...) + // Rewind to the first frame of the failed tx + // -- the frames are ordered, and we want to send them + // all again. + c.channelBuilder.RewindFrameCursor(data.Frames()[0]) delete(c.pendingTransactions, id) } else { c.log.Warn("unknown transaction marked as failed", "id", id) @@ -61,18 +61,16 @@ func (c *channel) TxFailed(id string) { c.metr.RecordBatchTxFailed() } -// TxConfirmed marks a transaction as confirmed on L1. Unfortunately even if all frames in -// a channel have been marked as confirmed on L1 the channel may be invalid & need to be -// resubmitted. -// This function may reset the pending channel if the pending channel has timed out. -func (c *channel) TxConfirmed(id string, inclusionBlock eth.BlockID) (bool, []*types.Block) { +// TxConfirmed marks a transaction as confirmed on L1. Returns a bool indicating +// whether the channel timed out on chain. +func (c *channel) TxConfirmed(id string, inclusionBlock eth.BlockID) bool { c.metr.RecordBatchTxSubmitted() c.log.Debug("marked transaction as confirmed", "id", id, "block", inclusionBlock) if _, ok := c.pendingTransactions[id]; !ok { c.log.Warn("unknown transaction marked as confirmed", "id", id, "block", inclusionBlock) // TODO: This can occur if we clear the channel while there are still pending transactions // We need to keep track of stale transactions instead - return false, nil + return false } delete(c.pendingTransactions, id) c.confirmedTransactions[id] = inclusionBlock @@ -82,21 +80,20 @@ func (c *channel) TxConfirmed(id string, inclusionBlock eth.BlockID) (bool, []*t c.minInclusionBlock = min(c.minInclusionBlock, inclusionBlock.Number) c.maxInclusionBlock = max(c.maxInclusionBlock, inclusionBlock.Number) + if c.isFullySubmitted() { + c.metr.RecordChannelFullySubmitted(c.ID()) + c.log.Info("Channel is fully submitted", "id", c.ID(), "min_inclusion_block", c.minInclusionBlock, "max_inclusion_block", c.maxInclusionBlock) + } + // If this channel timed out, put the pending blocks back into the local saved blocks // and then reset this state so it can try to build a new channel. if c.isTimedOut() { c.metr.RecordChannelTimedOut(c.ID()) c.log.Warn("Channel timed out", "id", c.ID(), "min_inclusion_block", c.minInclusionBlock, "max_inclusion_block", c.maxInclusionBlock) - return true, c.channelBuilder.Blocks() - } - // If we are done with this channel, record that. - if c.isFullySubmitted() { - c.metr.RecordChannelFullySubmitted(c.ID()) - c.log.Info("Channel is fully submitted", "id", c.ID(), "min_inclusion_block", c.minInclusionBlock, "max_inclusion_block", c.maxInclusionBlock) - return true, nil + return true } - return false, nil + return false } // Timeout returns the channel timeout L1 block number. If there is no timeout set, it returns 0. @@ -136,7 +133,7 @@ func (c *channel) ID() derive.ChannelID { func (c *channel) NextTxData() txData { nf := c.cfg.MaxFramesPerTx() txdata := txData{frames: make([]frameData, 0, nf), asBlob: c.cfg.UseBlobs} - for i := 0; i < nf && c.channelBuilder.HasFrame(); i++ { + for i := 0; i < nf && c.channelBuilder.HasPendingFrame(); i++ { frame := c.channelBuilder.NextFrame() txdata.frames = append(txdata.frames, frame) } @@ -151,7 +148,7 @@ func (c *channel) NextTxData() txData { func (c *channel) HasTxData() bool { if c.IsFull() || // If the channel is full, we should start to submit it !c.cfg.UseBlobs { // If using calldata, we only send one frame per tx - return c.channelBuilder.HasFrame() + return c.channelBuilder.HasPendingFrame() } // Collect enough frames if channel is not full yet return c.channelBuilder.PendingFrames() >= int(c.cfg.MaxFramesPerTx()) diff --git a/op-batcher/batcher/channel_builder.go b/op-batcher/batcher/channel_builder.go index ae1fb03d2841..597b5ed3e144 100644 --- a/op-batcher/batcher/channel_builder.go +++ b/op-batcher/batcher/channel_builder.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/queue" "github.com/ethereum/go-ethereum/core/types" ) @@ -65,7 +66,7 @@ type ChannelBuilder struct { // current channel co derive.ChannelOut // list of blocks in the channel. Saved in case the channel must be rebuilt - blocks []*types.Block + blocks queue.Queue[*types.Block] // latestL1Origin is the latest L1 origin of all the L2 blocks that have been added to the channel latestL1Origin eth.BlockID // oldestL1Origin is the oldest L1 origin of all the L2 blocks that have been added to the channel @@ -75,7 +76,12 @@ type ChannelBuilder struct { // oldestL2 is the oldest L2 block of all the L2 blocks that have been added to the channel oldestL2 eth.BlockID // frames data queue, to be send as txs - frames []frameData + frames queue.Queue[frameData] + // frameCursor tracks which frames in the queue were submitted + // frames[frameCursor] is the next unsubmitted (pending) frame + // frameCursor = len(frames) is reserved for when + // there are no pending (next unsubmitted) frames + frameCursor int // total frames counter numFrames int // total amount of output data of all frames created yet @@ -190,7 +196,7 @@ func (c *ChannelBuilder) AddBlock(block *types.Block) (*derive.L1BlockInfo, erro return l1info, fmt.Errorf("adding block to channel out: %w", err) } - c.blocks = append(c.blocks, block) + c.blocks.Enqueue(block) c.updateSwTimeout(l1info.Number) if l1info.Number > c.latestL1Origin.Number { @@ -312,11 +318,11 @@ func (c *ChannelBuilder) setFullErr(err error) { } // OutputFrames creates new frames with the channel out. It should be called -// after AddBlock and before iterating over available frames with HasFrame and +// after AddBlock and before iterating over pending frames with HasFrame and // NextFrame. // // If the channel isn't full yet, it will conservatively only -// pull readily available frames from the compression output. +// pull pending frames from the compression output. // If it is full, the channel is closed and all remaining // frames will be created, possibly with a small leftover frame. func (c *ChannelBuilder) OutputFrames() error { @@ -387,7 +393,7 @@ func (c *ChannelBuilder) outputFrame() error { id: frameID{chID: c.co.ID(), frameNumber: fn}, data: buf.Bytes(), } - c.frames = append(c.frames, frame) + c.frames.Enqueue(frame) c.numFrames++ c.outputBytes += len(frame.data) return err // possibly io.EOF (last frame) @@ -402,46 +408,47 @@ func (c *ChannelBuilder) Close() { } // TotalFrames returns the total number of frames that were created in this channel so far. -// It does not decrease when the frames queue is being emptied. func (c *ChannelBuilder) TotalFrames() int { return c.numFrames } -// HasFrame returns whether there's any available frame. If true, it can be -// popped using NextFrame(). +// HasPendingFrame returns whether there's any pending frame. If true, it can be +// dequeued using NextFrame(). // // Call OutputFrames before to create new frames from the channel out // compression pipeline. -func (c *ChannelBuilder) HasFrame() bool { - return len(c.frames) > 0 +func (c *ChannelBuilder) HasPendingFrame() bool { + return c.frameCursor < c.frames.Len() } // PendingFrames returns the number of pending frames in the frames queue. -// It is larger zero iff HasFrame() returns true. +// It is larger than zero iff HasFrame() returns true. func (c *ChannelBuilder) PendingFrames() int { - return len(c.frames) + return c.frames.Len() - c.frameCursor } -// NextFrame dequeues the next available frame. -// HasFrame must be called prior to check if there's a next frame available. +// NextFrame returns the next pending frame and increments the frameCursor +// HasFrame must be called prior to check if there a next pending frame exists. // Panics if called when there's no next frame. func (c *ChannelBuilder) NextFrame() frameData { - if len(c.frames) == 0 { + if len(c.frames) <= c.frameCursor { panic("no next frame") } - - f := c.frames[0] - c.frames = c.frames[1:] + f := c.frames[c.frameCursor] + c.frameCursor++ return f } -// PushFrames adds the frames back to the internal frames queue. Panics if not of -// the same channel. -func (c *ChannelBuilder) PushFrames(frames ...frameData) { - for _, f := range frames { - if f.id.chID != c.ID() { - panic("wrong channel") - } - c.frames = append(c.frames, f) +// RewindFrameCursor moves the frameCursor to point at the supplied frame +// only if it is ahead of it. +// Panics if the frame is not in this channel. +func (c *ChannelBuilder) RewindFrameCursor(frame frameData) { + if c.frames.Len() <= int(frame.id.frameNumber) || + len(c.frames[frame.id.frameNumber].data) != len(frame.data) || + c.frames[frame.id.frameNumber].id.chID != frame.id.chID { + panic("cannot rewind to unknown frame") + } + if c.frameCursor > int(frame.id.frameNumber) { + c.frameCursor = int(frame.id.frameNumber) } } diff --git a/op-batcher/batcher/channel_builder_test.go b/op-batcher/batcher/channel_builder_test.go index 957f9ae59739..6994186b7f07 100644 --- a/op-batcher/batcher/channel_builder_test.go +++ b/op-batcher/batcher/channel_builder_test.go @@ -299,6 +299,7 @@ func TestChannelBuilderBatchType(t *testing.T) { {"ChannelBuilder_PendingFrames_TotalFrames", ChannelBuilder_PendingFrames_TotalFrames}, {"ChannelBuilder_InputBytes", ChannelBuilder_InputBytes}, {"ChannelBuilder_OutputBytes", ChannelBuilder_OutputBytes}, + {"ChannelBuilder_OutputWrongFramePanic", ChannelBuilder_OutputWrongFramePanic}, } for _, test := range tests { test := test @@ -340,7 +341,7 @@ func TestChannelBuilder_NextFrame(t *testing.T) { }, data: expectedBytes, } - cb.PushFrames(frameData) + cb.frames = append(cb.frames, frameData) // There should only be 1 frame in the channel builder require.Equal(t, 1, cb.PendingFrames()) @@ -355,7 +356,7 @@ func TestChannelBuilder_NextFrame(t *testing.T) { require.PanicsWithValue(t, "no next frame", func() { cb.NextFrame() }) } -// TestChannelBuilder_OutputWrongFramePanic tests that a panic is thrown when a frame is pushed with an invalid frame id +// TestChannelBuilder_OutputWrongFramePanic tests that a panic is thrown when we try to rewind the cursor with an invalid frame id func ChannelBuilder_OutputWrongFramePanic(t *testing.T, batchType uint) { channelConfig := defaultTestChannelConfig() channelConfig.BatchType = batchType @@ -377,7 +378,7 @@ func ChannelBuilder_OutputWrongFramePanic(t *testing.T, batchType uint) { // The frame push should panic since we constructed a new channel out // so the channel out id won't match - require.PanicsWithValue(t, "wrong channel", func() { + require.PanicsWithValue(t, "cannot rewind to unknown frame", func() { frame := frameData{ id: frameID{ chID: co.ID(), @@ -385,7 +386,7 @@ func ChannelBuilder_OutputWrongFramePanic(t *testing.T, batchType uint) { }, data: buf.Bytes(), } - cb.PushFrames(frame) + cb.RewindFrameCursor(frame) }) } @@ -625,11 +626,11 @@ func TestChannelBuilder_FullShadowCompressor(t *testing.T) { require.NoError(cb.OutputFrames()) - require.True(cb.HasFrame()) + require.True(cb.HasPendingFrame()) f := cb.NextFrame() require.Less(len(f.data), int(cfg.MaxFrameSize)) // would fail without fix, full frame - require.False(cb.HasFrame(), "no leftover frame expected") // would fail without fix + require.False(cb.HasPendingFrame(), "no leftover frame expected") // would fail without fix } func ChannelBuilder_AddBlock(t *testing.T, batchType uint) { @@ -656,8 +657,8 @@ func ChannelBuilder_AddBlock(t *testing.T, batchType uint) { expectedInputBytes = 47 } require.Equal(t, expectedInputBytes, cb.co.InputBytes()) - require.Equal(t, 1, len(cb.blocks)) - require.Equal(t, 0, len(cb.frames)) + require.Equal(t, 1, cb.blocks.Len()) + require.Equal(t, 0, cb.frames.Len()) require.True(t, cb.IsFull()) // Since the channel output is full, the next call to AddBlock @@ -858,7 +859,7 @@ func ChannelBuilder_PendingFrames_TotalFrames(t *testing.T, batchType uint) { // empty queue for pf := nf - 1; pf >= 0; pf-- { - require.True(cb.HasFrame()) + require.True(cb.HasPendingFrame()) _ = cb.NextFrame() require.Equal(cb.PendingFrames(), pf) require.Equal(cb.TotalFrames(), nf) @@ -932,7 +933,7 @@ func ChannelBuilder_OutputBytes(t *testing.T, batchType uint) { require.Greater(cb.PendingFrames(), 1) var flen int - for cb.HasFrame() { + for cb.HasPendingFrame() { f := cb.NextFrame() flen += len(f.data) } diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 8e9870413a40..4208a8cd3795 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -39,6 +39,10 @@ type channelManager struct { // All blocks since the last request for new tx data. blocks queue.Queue[*types.Block] + // blockCursor is an index into blocks queue. It points at the next block + // to build a channel with. blockCursor = len(blocks) is reserved for when + // there are no blocks ready to build with. + blockCursor int // The latest L1 block from all the L2 blocks in the most recently submitted channel. // Used to track channel duration timeouts. l1OriginLastSubmittedChannel eth.BlockID @@ -53,9 +57,6 @@ type channelManager struct { channelQueue []*channel // used to lookup channels by tx ID upon tx success / failure txChannels map[string]*channel - - // if set to true, prevents production of any new channel frames - closed bool } func NewChannelManager(log log.Logger, metr metrics.Metricer, cfgProvider ChannelConfigProvider, rollupCfg *rollup.Config) *channelManager { @@ -81,14 +82,18 @@ func (s *channelManager) Clear(l1OriginLastSubmittedChannel eth.BlockID) { defer s.mu.Unlock() s.log.Trace("clearing channel manager state") s.blocks.Clear() + s.blockCursor = 0 s.l1OriginLastSubmittedChannel = l1OriginLastSubmittedChannel s.tip = common.Hash{} - s.closed = false s.currentChannel = nil s.channelQueue = nil s.txChannels = make(map[string]*channel) } +func (s *channelManager) pendingBlocks() int { + return s.blocks.Len() - s.blockCursor +} + // TxFailed records a transaction as failed. It will attempt to resubmit the data // in the failed transaction. func (s *channelManager) TxFailed(_id txID) { @@ -98,34 +103,21 @@ func (s *channelManager) TxFailed(_id txID) { if channel, ok := s.txChannels[id]; ok { delete(s.txChannels, id) channel.TxFailed(id) - if s.closed && channel.NoneSubmitted() { - s.log.Info("Channel has no submitted transactions, clearing for shutdown", "chID", channel.ID()) - s.removePendingChannel(channel) - } } else { s.log.Warn("transaction from unknown channel marked as failed", "id", id) } } -// TxConfirmed marks a transaction as confirmed on L1. Unfortunately even if all frames in -// a channel have been marked as confirmed on L1 the channel may be invalid & need to be -// resubmitted. -// This function may reset the pending channel if the pending channel has timed out. +// TxConfirmed marks a transaction as confirmed on L1. Only if the channel timed out +// the channelManager's state is modified. func (s *channelManager) TxConfirmed(_id txID, inclusionBlock eth.BlockID) { s.mu.Lock() defer s.mu.Unlock() id := _id.String() if channel, ok := s.txChannels[id]; ok { delete(s.txChannels, id) - done, blocksToRequeue := channel.TxConfirmed(id, inclusionBlock) - if done { - s.removePendingChannel(channel) - if len(blocksToRequeue) > 0 { - s.blocks.Prepend(blocksToRequeue...) - } - for _, b := range blocksToRequeue { - s.metr.RecordL2BlockInPendingQueue(b) - } + if timedOut := channel.TxConfirmed(id, inclusionBlock); timedOut { + s.handleChannelInvalidated(channel) } } else { s.log.Warn("transaction from unknown channel marked as confirmed", "id", id) @@ -134,23 +126,48 @@ func (s *channelManager) TxConfirmed(_id txID, inclusionBlock eth.BlockID) { s.log.Debug("marked transaction as confirmed", "id", id, "block", inclusionBlock) } -// removePendingChannel removes the given completed channel from the manager's state. -func (s *channelManager) removePendingChannel(channel *channel) { - if s.currentChannel == channel { - s.currentChannel = nil +// rewindToBlock updates the blockCursor to point at +// the block with the supplied hash, only if that block exists +// in the block queue and the blockCursor is ahead of it. +// Panics if the block is not in state. +func (s *channelManager) rewindToBlock(block eth.BlockID) { + idx := block.Number - s.blocks[0].Number().Uint64() + if s.blocks[idx].Hash() == block.Hash && idx < uint64(s.blockCursor) { + s.blockCursor = int(idx) + } else { + panic("tried to rewind to nonexistent block") } - index := -1 - for i, c := range s.channelQueue { - if c == channel { - index = i - break +} + +// handleChannelInvalidated rewinds the channelManager's blockCursor +// to point at the first block added to the provided channel, +// and removes the channel from the channelQueue, along with +// any channels which are newer than the provided channel. +func (s *channelManager) handleChannelInvalidated(c *channel) { + if len(c.channelBuilder.blocks) > 0 { + // This is usually true, but there is an edge case + // where a channel timed out before any blocks got added. + // In that case we end up with an empty frame (header only), + // and there are no blocks to requeue. + blockID := eth.ToBlockID(c.channelBuilder.blocks[0]) + for _, block := range c.channelBuilder.blocks { + s.metr.RecordL2BlockInPendingQueue(block) } + s.rewindToBlock(blockID) + } else { + s.log.Debug("channelManager.handleChanneInvalidated: channel had no blocks") } - if index < 0 { - s.log.Warn("channel not found in channel queue", "id", channel.ID()) - return + + // Trim provided channel and any older channels: + for i := range s.channelQueue { + if s.channelQueue[i] == c { + s.channelQueue = s.channelQueue[:i] + break + } } - s.channelQueue = append(s.channelQueue[:index], s.channelQueue[index+1:]...) + + // We want to start writing to a new channel, so reset currentChannel. + s.currentChannel = nil } // nextTxData dequeues frames from the channel and returns them encoded in a transaction. @@ -207,7 +224,16 @@ func (s *channelManager) TxData(l1Head eth.BlockID) (txData, error) { s.log.Info("Recomputing optimal ChannelConfig: changing DA type and requeing blocks...", "useBlobsBefore", s.defaultCfg.UseBlobs, "useBlobsAfter", newCfg.UseBlobs) - s.Requeue(newCfg) + + // Invalidate the channel so its blocks + // get requeued: + s.handleChannelInvalidated(channel) + + // Set the defaultCfg so new channels + // pick up the new ChannelConfig + s.defaultCfg = newCfg + + // Try again to get data to send on chain. channel, err = s.getReadyChannel(l1Head) if err != nil { return emptyTxData, err @@ -238,14 +264,9 @@ func (s *channelManager) getReadyChannel(l1Head eth.BlockID) (*channel, error) { return firstWithTxData, nil } - if s.closed { - return nil, io.EOF - } - // No pending tx data, so we have to add new blocks to the channel - // If we have no saved blocks, we will not be able to create valid frames - if s.blocks.Len() == 0 { + if s.pendingBlocks() == 0 { return nil, io.EOF } @@ -299,8 +320,8 @@ func (s *channelManager) ensureChannelWithSpace(l1Head eth.BlockID) error { s.log.Info("Created channel", "id", pc.ID(), "l1Head", l1Head, + "blocks_pending", s.pendingBlocks(), "l1OriginLastSubmittedChannel", s.l1OriginLastSubmittedChannel, - "blocks_pending", s.blocks.Len(), "batch_type", cfg.BatchType, "compression_algo", cfg.CompressorConfig.CompressionAlgo, "target_num_frames", cfg.TargetNumFrames, @@ -331,7 +352,7 @@ func (s *channelManager) processBlocks() error { latestL2ref eth.L2BlockRef ) - for i := 0; ; i++ { + for i := s.blockCursor; ; i++ { block, ok := s.blocks.PeekN(i) if !ok { break @@ -355,7 +376,7 @@ func (s *channelManager) processBlocks() error { } } - _, _ = s.blocks.DequeueN(blocksAdded) + s.blockCursor += blocksAdded s.metr.RecordL2BlocksAdded(latestL2ref, blocksAdded, @@ -364,7 +385,7 @@ func (s *channelManager) processBlocks() error { s.currentChannel.ReadyBytes()) s.log.Debug("Added blocks to channel", "blocks_added", blocksAdded, - "blocks_pending", s.blocks.Len(), + "blocks_pending", s.pendingBlocks(), "channel_full", s.currentChannel.IsFull(), "input_bytes", s.currentChannel.InputBytes(), "ready_bytes", s.currentChannel.ReadyBytes(), @@ -384,7 +405,7 @@ func (s *channelManager) outputFrames() error { inBytes, outBytes := s.currentChannel.InputBytes(), s.currentChannel.OutputBytes() s.metr.RecordChannelClosed( s.currentChannel.ID(), - s.blocks.Len(), + s.pendingBlocks(), s.currentChannel.TotalFrames(), inBytes, outBytes, @@ -398,7 +419,7 @@ func (s *channelManager) outputFrames() error { s.log.Info("Channel closed", "id", s.currentChannel.ID(), - "blocks_pending", s.blocks.Len(), + "blocks_pending", s.pendingBlocks(), "num_frames", s.currentChannel.TotalFrames(), "input_bytes", inBytes, "output_bytes", outBytes, @@ -443,83 +464,77 @@ func l2BlockRefFromBlockAndL1Info(block *types.Block, l1info *derive.L1BlockInfo var ErrPendingAfterClose = errors.New("pending channels remain after closing channel-manager") -// Close clears any pending channels that are not in-flight already, to leave a clean derivation state. -// Close then marks the remaining current open channel, if any, as "full" so it can be submitted as well. -// Close does NOT immediately output frames for the current remaining channel: -// as this might error, due to limitations on a single channel. -// Instead, this is part of the pending-channel submission work: after closing, -// the caller SHOULD drain pending channels by generating TxData repeatedly until there is none left (io.EOF). -// A ErrPendingAfterClose error will be returned if there are any remaining pending channels to submit. -func (s *channelManager) Close() error { - s.mu.Lock() - defer s.mu.Unlock() - if s.closed { - return nil +// pruneSafeBlocks dequeues blocks from the internal blocks queue +// if they have now become safe. +func (s *channelManager) pruneSafeBlocks(newSafeHead eth.L2BlockRef) { + oldestBlock, ok := s.blocks.Peek() + if !ok { + // no blocks to prune + return } - s.closed = true - s.log.Info("Channel manager is closing") - - // Any pending state can be proactively cleared if there are no submitted transactions - for _, ch := range s.channelQueue { - if ch.NoneSubmitted() { - s.log.Info("Channel has no past or pending submission - dropping", "id", ch.ID()) - s.removePendingChannel(ch) - } else { - s.log.Info("Channel is in-flight and will need to be submitted after close", "id", ch.ID(), "confirmed", len(ch.confirmedTransactions), "pending", len(ch.pendingTransactions)) - } + if newSafeHead.Number+1 == oldestBlock.NumberU64() { + // no blocks to prune + return } - s.log.Info("Reviewed all pending channels on close", "remaining", len(s.channelQueue)) - if s.currentChannel == nil { - return nil + if newSafeHead.Number+1 < oldestBlock.NumberU64() { + // This could happen if there was an L1 reorg. + s.log.Warn("safe head reversed, clearing channel manager state", + "oldestBlock", eth.ToBlockID(oldestBlock), + "newSafeBlock", newSafeHead) + // We should restart work from the new safe head, + // and therefore prune all the blocks. + s.Clear(newSafeHead.L1Origin) + return } - // If the channel is already full, we don't need to close it or output frames. - // This would already have happened in TxData. - if !s.currentChannel.IsFull() { - // Force-close the remaining open channel early (if not already closed): - // it will be marked as "full" due to service termination. - s.currentChannel.Close() + numBlocksToDequeue := newSafeHead.Number + 1 - oldestBlock.NumberU64() - // Final outputFrames call in case there was unflushed data in the compressor. - if err := s.outputFrames(); err != nil { - return fmt.Errorf("outputting frames during close: %w", err) - } + if numBlocksToDequeue > uint64(s.blocks.Len()) { + // This could happen if the batcher restarted. + // The sequencer may have derived the safe chain + // from channels sent by a previous batcher instance. + s.log.Warn("safe head above unsafe head, clearing channel manager state", + "unsafeBlock", eth.ToBlockID(s.blocks[s.blocks.Len()-1]), + "newSafeBlock", newSafeHead) + // We should restart work from the new safe head, + // and therefore prune all the blocks. + s.Clear(newSafeHead.L1Origin) + return } - if s.currentChannel.HasTxData() { - // Make it clear to the caller that there is remaining pending work. - return ErrPendingAfterClose + if s.blocks[numBlocksToDequeue-1].Hash() != newSafeHead.Hash { + s.log.Warn("safe chain reorg, clearing channel manager state", + "existingBlock", eth.ToBlockID(s.blocks[numBlocksToDequeue-1]), + "newSafeBlock", newSafeHead) + // We should restart work from the new safe head, + // and therefore prune all the blocks. + s.Clear(newSafeHead.L1Origin) + return } - return nil -} -// Requeue rebuilds the channel manager state by -// rewinding blocks back from the channel queue, and setting the defaultCfg. -func (s *channelManager) Requeue(newCfg ChannelConfig) { - newChannelQueue := []*channel{} - blocksToRequeue := []*types.Block{} - for _, channel := range s.channelQueue { - if !channel.NoneSubmitted() { - newChannelQueue = append(newChannelQueue, channel) - continue - } - blocksToRequeue = append(blocksToRequeue, channel.channelBuilder.Blocks()...) - } + // This shouldn't return an error because + // We already checked numBlocksToDequeue <= s.blocks.Len() + _, _ = s.blocks.DequeueN(int(numBlocksToDequeue)) + s.blockCursor -= int(numBlocksToDequeue) - // We put the blocks back at the front of the queue: - s.blocks.Prepend(blocksToRequeue...) - for _, b := range blocksToRequeue { - s.metr.RecordL2BlockInPendingQueue(b) + if s.blockCursor < 0 { + panic("negative blockCursor") } +} - // Channels which where already being submitted are put back - s.channelQueue = newChannelQueue - s.currentChannel = nil - // Setting the defaultCfg will cause new channels - // to pick up the new ChannelConfig - s.defaultCfg = newCfg +// pruneChannels dequeues channels from the internal channels queue +// if they were built using blocks which are now safe +func (s *channelManager) pruneChannels(newSafeHead eth.L2BlockRef) { + i := 0 + for _, ch := range s.channelQueue { + if ch.LatestL2().Number > newSafeHead.Number { + break + } + i++ + } + s.channelQueue = s.channelQueue[i:] } // PendingDABytes returns the current number of bytes pending to be written to the DA layer (from blocks fetched from L2 diff --git a/op-batcher/batcher/channel_manager_test.go b/op-batcher/batcher/channel_manager_test.go index d3a67a1bf1f4..1c742207a5f0 100644 --- a/op-batcher/batcher/channel_manager_test.go +++ b/op-batcher/batcher/channel_manager_test.go @@ -40,10 +40,6 @@ func TestChannelManagerBatchType(t *testing.T) { {"ChannelManagerReturnsErrReorgWhenDrained", ChannelManagerReturnsErrReorgWhenDrained}, {"ChannelManager_Clear", ChannelManager_Clear}, {"ChannelManager_TxResend", ChannelManager_TxResend}, - {"ChannelManagerCloseBeforeFirstUse", ChannelManagerCloseBeforeFirstUse}, - {"ChannelManagerCloseNoPendingChannel", ChannelManagerCloseNoPendingChannel}, - {"ChannelManagerClosePendingChannel", ChannelManagerClosePendingChannel}, - {"ChannelManagerCloseAllTxsFailed", ChannelManagerCloseAllTxsFailed}, } for _, test := range tests { test := test @@ -154,14 +150,13 @@ func ChannelManager_Clear(t *testing.T, batchType uint) { // Process the blocks // We should have a pending channel with 1 frame - // and no more blocks since processBlocks consumes - // the list + require.NoError(m.processBlocks()) require.NoError(m.currentChannel.channelBuilder.co.Flush()) require.NoError(m.outputFrames()) _, err := m.nextTxData(m.currentChannel) require.NoError(err) - require.Len(m.blocks, 0) + require.Equal(m.blockCursor, len(m.blocks)) require.NotNil(m.l1OriginLastSubmittedChannel) require.Equal(newL1Tip, m.tip) require.Len(m.currentChannel.pendingTransactions, 1) @@ -173,7 +168,7 @@ func ChannelManager_Clear(t *testing.T, batchType uint) { ParentHash: a.Hash(), }, nil, nil, nil) require.NoError(m.AddL2Block(b)) - require.Len(m.blocks, 1) + require.Equal(m.blockCursor, len(m.blocks)-1) require.Equal(b.Hash(), m.tip) safeL1Origin := eth.BlockID{ @@ -228,220 +223,6 @@ func ChannelManager_TxResend(t *testing.T, batchType uint) { require.Len(fs, 1) } -// ChannelManagerCloseBeforeFirstUse ensures that the channel manager -// will not produce any frames if closed immediately. -func ChannelManagerCloseBeforeFirstUse(t *testing.T, batchType uint) { - require := require.New(t) - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - log := testlog.Logger(t, log.LevelCrit) - m := NewChannelManager(log, metrics.NoopMetrics, - channelManagerTestConfig(10000, batchType), - defaultTestRollupConfig, - ) - m.Clear(eth.BlockID{}) - - a := derivetest.RandomL2BlockWithChainId(rng, 4, defaultTestRollupConfig.L2ChainID) - - require.NoError(m.Close(), "Expected to close channel manager gracefully") - - err := m.AddL2Block(a) - require.NoError(err, "Failed to add L2 block") - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected closed channel manager to contain no tx data") -} - -// ChannelManagerCloseNoPendingChannel ensures that the channel manager -// can gracefully close with no pending channels, and will not emit any new -// channel frames. -func ChannelManagerCloseNoPendingChannel(t *testing.T, batchType uint) { - require := require.New(t) - log := testlog.Logger(t, log.LevelCrit) - cfg := channelManagerTestConfig(10000, batchType) - cfg.CompressorConfig.TargetOutputSize = 1 // full on first block - cfg.ChannelTimeout = 1000 - m := NewChannelManager(log, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - m.Clear(eth.BlockID{}) - a := newMiniL2Block(0) - b := newMiniL2BlockWithNumberParent(0, big.NewInt(1), a.Hash()) - - err := m.AddL2Block(a) - require.NoError(err, "Failed to add L2 block") - - txdata, err := m.TxData(eth.BlockID{}) - require.NoError(err, "Expected channel manager to return valid tx data") - - m.TxConfirmed(txdata.ID(), eth.BlockID{}) - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected channel manager to EOF") - - require.NoError(m.Close(), "Expected to close channel manager gracefully") - - err = m.AddL2Block(b) - require.NoError(err, "Failed to add L2 block") - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected closed channel manager to return no new tx data") -} - -// ChannelManagerClosePendingChannel ensures that the channel manager -// can gracefully close with a pending channel, and will not produce any -// new channel frames after this point. -func ChannelManagerClosePendingChannel(t *testing.T, batchType uint) { - require := require.New(t) - // The number of batch txs depends on compression of the random data, hence the static test RNG seed. - // Example of different RNG seed that creates less than 2 frames: 1698700588902821588 - rng := rand.New(rand.NewSource(123)) - log := testlog.Logger(t, log.LevelError) - cfg := channelManagerTestConfig(10_000, batchType) - cfg.ChannelTimeout = 1000 - m := NewChannelManager(log, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - m.Clear(eth.BlockID{}) - - numTx := 20 // Adjust number of txs to make 2 frames - a := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) - - err := m.AddL2Block(a) - require.NoError(err, "Failed to add L2 block") - - txdata, err := m.TxData(eth.BlockID{}) - require.NoError(err, "Expected channel manager to produce valid tx data") - log.Info("generated first tx data", "len", txdata.Len()) - - m.TxConfirmed(txdata.ID(), eth.BlockID{}) - - require.ErrorIs(m.Close(), ErrPendingAfterClose, "Expected channel manager to error on close because of pending tx data") - - txdata, err = m.TxData(eth.BlockID{}) - require.NoError(err, "Expected channel manager to produce tx data from remaining L2 block data") - log.Info("generated more tx data", "len", txdata.Len()) - - m.TxConfirmed(txdata.ID(), eth.BlockID{}) - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected channel manager to have no more tx data") - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected closed channel manager to produce no more tx data") -} - -// ChannelManager_Close_PartiallyPendingChannel ensures that the channel manager -// can gracefully close with a pending channel, where a block is still waiting -// inside the compressor to be flushed. -// -// This test runs only for singular batches on purpose. -// The SpanChannelOut writes full span batches to the compressor for -// every new block that's added, so NonCompressor cannot be used to -// set up a scenario where data is only partially flushed. -// Couldn't get the test to work even with modifying NonCompressor -// to flush half-way through writing to the compressor... -func TestChannelManager_Close_PartiallyPendingChannel(t *testing.T) { - require := require.New(t) - // The number of batch txs depends on compression of the random data, hence the static test RNG seed. - // Example of different RNG seed that creates less than 2 frames: 1698700588902821588 - rng := rand.New(rand.NewSource(123)) - log := testlog.Logger(t, log.LevelError) - cfg := ChannelConfig{ - MaxFrameSize: 2200, - ChannelTimeout: 1000, - TargetNumFrames: 100, - } - cfg.InitNoneCompressor() - m := NewChannelManager(log, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - m.Clear(eth.BlockID{}) - - numTx := 3 // Adjust number of txs to make 2 frames - a := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) - b := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) - bHeader := b.Header() - bHeader.Number = new(big.Int).Add(a.Number(), big.NewInt(1)) - bHeader.ParentHash = a.Hash() - b = b.WithSeal(bHeader) - - require.NoError(m.AddL2Block(a), "adding 1st L2 block") - require.NoError(m.AddL2Block(b), "adding 2nd L2 block") - - // Inside TxData, the two blocks queued above are written to the compressor. - // The NonCompressor will flush the first, but not the second block, when - // adding the second block, setting up the test with a partially flushed - // compressor. - txdata, err := m.TxData(eth.BlockID{}) - require.NoError(err, "Expected channel manager to produce valid tx data") - log.Info("generated first tx data", "len", txdata.Len()) - - m.TxConfirmed(txdata.ID(), eth.BlockID{}) - - // ensure no new ready data before closing - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected unclosed channel manager to only return a single frame") - - require.ErrorIs(m.Close(), ErrPendingAfterClose, "Expected channel manager to error on close because of pending tx data") - require.NotNil(m.currentChannel) - require.ErrorIs(m.currentChannel.FullErr(), ErrTerminated, "Expected current channel to be terminated by Close") - - txdata, err = m.TxData(eth.BlockID{}) - require.NoError(err, "Expected channel manager to produce tx data from remaining L2 block data") - log.Info("generated more tx data", "len", txdata.Len()) - - m.TxConfirmed(txdata.ID(), eth.BlockID{}) - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected closed channel manager to produce no more tx data") -} - -// ChannelManagerCloseAllTxsFailed ensures that the channel manager -// can gracefully close after producing transaction frames if none of these -// have successfully landed on chain. -func ChannelManagerCloseAllTxsFailed(t *testing.T, batchType uint) { - require := require.New(t) - rng := rand.New(rand.NewSource(1357)) - log := testlog.Logger(t, log.LevelCrit) - cfg := channelManagerTestConfig(100, batchType) - cfg.TargetNumFrames = 1000 - cfg.InitNoneCompressor() - m := NewChannelManager(log, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - m.Clear(eth.BlockID{}) - - a := derivetest.RandomL2BlockWithChainId(rng, 1000, defaultTestRollupConfig.L2ChainID) - - err := m.AddL2Block(a) - require.NoError(err, "Failed to add L2 block") - - drainTxData := func() (txdatas []txData) { - for { - txdata, err := m.TxData(eth.BlockID{}) - if err == io.EOF { - return - } - require.NoError(err, "Expected channel manager to produce valid tx data") - txdatas = append(txdatas, txdata) - } - } - - txdatas := drainTxData() - require.NotEmpty(txdatas) - - for _, txdata := range txdatas { - m.TxFailed(txdata.ID()) - } - - // Show that this data will continue to be emitted as long as the transaction - // fails and the channel manager is not closed - txdatas1 := drainTxData() - require.NotEmpty(txdatas) - require.ElementsMatch(txdatas, txdatas1, "expected same txdatas on re-attempt") - - for _, txdata := range txdatas1 { - m.TxFailed(txdata.ID()) - } - - require.NoError(m.Close(), "Expected to close channel manager gracefully") - - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(err, io.EOF, "Expected closed channel manager to produce no more tx data") -} - func TestChannelManager_ChannelCreation(t *testing.T) { l := testlog.Logger(t, log.LevelCrit) const maxChannelDuration = 15 @@ -543,10 +324,12 @@ func TestChannelManager_TxData(t *testing.T) { // * One when the channelManager was created // * One when the channel is about to be submitted - // * Potentially one more if the replacement channel is about to be submitted, - // this only happens when going from calldata->blobs because - // the channel is no longer ready to send until more data - // is added. + // * Potentially one more when the replacement channel + // is not immediately ready to be submitted, but later + // becomes ready after more data is added. + // This only happens when going from calldata->blobs because + // the channel is not immediately ready to send until more data + // is added due to blob channels having greater capacity. numExpectedAssessments int } @@ -591,7 +374,7 @@ func TestChannelManager_TxData(t *testing.T) { // we get some data to submit var data txData for { - m.blocks = []*types.Block{blockA} + m.blocks = append(m.blocks, blockA) data, err = m.TxData(eth.BlockID{}) if err == nil && data.Len() > 0 { break @@ -609,16 +392,15 @@ func TestChannelManager_TxData(t *testing.T) { } -// TestChannelManager_Requeue seeds the channel manager with blocks, +// TestChannelManager_handleChannelInvalidated seeds the channel manager with blocks, // takes a state snapshot, triggers the blocks->channels pipeline, -// and then calls Requeue. Finally, it asserts the channel manager's -// state is equal to the snapshot. It repeats this for a channel -// which has a pending transaction and verifies that Requeue is then -// a noop. -func TestChannelManager_Requeue(t *testing.T) { +// and then calls handleChannelInvalidated. It asserts on the final state of +// the channel manager. +func TestChannelManager_handleChannelInvalidated(t *testing.T) { l := testlog.Logger(t, log.LevelCrit) cfg := channelManagerTestConfig(100, derive.SingularBatchType) - m := NewChannelManager(l, metrics.NoopMetrics, cfg, defaultTestRollupConfig) + metrics := new(metrics.TestMetrics) + m := NewChannelManager(l, metrics, cfg, defaultTestRollupConfig) // Seed channel manager with blocks rng := rand.New(rand.NewSource(99)) @@ -631,50 +413,197 @@ func TestChannelManager_Requeue(t *testing.T) { m.blocks = stateSnapshot require.Empty(t, m.channelQueue) + // Place an old channel in the queue. + // This channel should not be affected by + // a requeue or a later channel timing out. + oldChannel := newChannel(l, nil, m.defaultCfg, defaultTestRollupConfig, 0, nil) + oldChannel.Close() + m.channelQueue = []*channel{oldChannel} + require.Len(t, m.channelQueue, 1) + + // Setup initial metrics + metrics.RecordL2BlockInPendingQueue(blockA) + metrics.RecordL2BlockInPendingQueue(blockB) + pendingBytesBefore := metrics.PendingBlocksBytesCurrent + // Trigger the blocks -> channelQueue data pipelining require.NoError(t, m.ensureChannelWithSpace(eth.BlockID{})) - require.NotEmpty(t, m.channelQueue) + require.Len(t, m.channelQueue, 2) require.NoError(t, m.processBlocks()) // Assert that at least one block was processed into the channel - require.NotContains(t, m.blocks, blockA) + require.Equal(t, 1, m.blockCursor) - l1OriginBeforeRequeue := m.l1OriginLastSubmittedChannel + // Check metric decreased + metricsDelta := metrics.PendingBlocksBytesCurrent - pendingBytesBefore + require.Negative(t, metricsDelta) - // Call the function we are testing - m.Requeue(m.defaultCfg) + l1OriginBefore := m.l1OriginLastSubmittedChannel + + m.handleChannelInvalidated(m.currentChannel) // Ensure we got back to the state above require.Equal(t, m.blocks, stateSnapshot) - require.Empty(t, m.channelQueue) + require.Contains(t, m.channelQueue, oldChannel) + require.Len(t, m.channelQueue, 1) + + // Check metric came back up to previous value + require.Equal(t, pendingBytesBefore, metrics.PendingBlocksBytesCurrent) // Ensure the l1OridingLastSubmittedChannel was // not changed. This ensures the next channel // has its duration timeout deadline computed // properly. - require.Equal(t, l1OriginBeforeRequeue, m.l1OriginLastSubmittedChannel) + require.Equal(t, l1OriginBefore, m.l1OriginLastSubmittedChannel) // Trigger the blocks -> channelQueue data pipelining again require.NoError(t, m.ensureChannelWithSpace(eth.BlockID{})) require.NotEmpty(t, m.channelQueue) require.NoError(t, m.processBlocks()) +} - // Assert that at least one block was processed into the channel - require.NotContains(t, m.blocks, blockA) +func TestChannelManager_PruneBlocks(t *testing.T) { + l := testlog.Logger(t, log.LevelDebug) + cfg := channelManagerTestConfig(100, derive.SingularBatchType) + m := NewChannelManager(l, metrics.NoopMetrics, cfg, defaultTestRollupConfig) + + a := types.NewBlock(&types.Header{ + Number: big.NewInt(0), + }, nil, nil, nil) + b := types.NewBlock(&types.Header{ // This will shortly become the safe head + Number: big.NewInt(1), + ParentHash: a.Hash(), + }, nil, nil, nil) + c := types.NewBlock(&types.Header{ + Number: big.NewInt(2), + ParentHash: b.Hash(), + }, nil, nil, nil) + + require.NoError(t, m.AddL2Block(a)) + m.blockCursor += 1 + require.NoError(t, m.AddL2Block(b)) + m.blockCursor += 1 + require.NoError(t, m.AddL2Block(c)) + m.blockCursor += 1 + + // Normal path + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: b.Hash(), + Number: b.NumberU64(), + }) + require.Equal(t, queue.Queue[*types.Block]{c}, m.blocks) + + // Safe chain didn't move, nothing to prune + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: b.Hash(), + Number: b.NumberU64(), + }) + require.Equal(t, queue.Queue[*types.Block]{c}, m.blocks) + + // Safe chain moved beyond the blocks we had + // state should be cleared + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: c.Hash(), + Number: uint64(99), + }) + require.Equal(t, queue.Queue[*types.Block]{}, m.blocks) + + // No blocks to prune, NOOP + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: c.Hash(), + Number: c.NumberU64(), + }) + require.Equal(t, queue.Queue[*types.Block]{}, m.blocks) + + // Put another block in + d := types.NewBlock(&types.Header{ + Number: big.NewInt(3), + ParentHash: c.Hash(), + }, nil, nil, nil) + require.NoError(t, m.AddL2Block(d)) + m.blockCursor += 1 + + // Safe chain reorg + // state should be cleared + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: a.Hash(), + Number: uint64(3), + }) + require.Equal(t, queue.Queue[*types.Block]{}, m.blocks) + + // Put another block in + require.NoError(t, m.AddL2Block(d)) + m.blockCursor += 1 + + // Safe chain reversed + // state should be cleared + m.pruneSafeBlocks(eth.L2BlockRef{ + Hash: a.Hash(), // unused + Number: uint64(1), + }) + require.Equal(t, queue.Queue[*types.Block]{}, m.blocks) + +} + +func TestChannelManager_PruneChannels(t *testing.T) { + l := testlog.Logger(t, log.LevelCrit) + cfg := channelManagerTestConfig(100, derive.SingularBatchType) + cfg.InitNoneCompressor() + m := NewChannelManager(l, metrics.NoopMetrics, cfg, defaultTestRollupConfig) - // Now mark the 0th channel in the queue as already - // starting to send on chain - channel0 := m.channelQueue[0] - channel0.pendingTransactions["foo"] = txData{} - require.False(t, channel0.NoneSubmitted()) + A, err := newChannelWithChannelOut(l, metrics.NoopMetrics, cfg, m.rollupCfg, 0) + require.NoError(t, err) + B, err := newChannelWithChannelOut(l, metrics.NoopMetrics, cfg, m.rollupCfg, 0) + require.NoError(t, err) + C, err := newChannelWithChannelOut(l, metrics.NoopMetrics, cfg, m.rollupCfg, 0) + require.NoError(t, err) + + m.channelQueue = []*channel{A, B, C} + + numTx := 1 + rng := rand.New(rand.NewSource(123)) + a0 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + a0 = a0.WithSeal(&types.Header{Number: big.NewInt(0)}) + a1 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + a1 = a1.WithSeal(&types.Header{Number: big.NewInt(1)}) + b2 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + b2 = b2.WithSeal(&types.Header{Number: big.NewInt(2)}) + b3 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + b3 = b3.WithSeal(&types.Header{Number: big.NewInt(3)}) + c4 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + c4 = c4.WithSeal(&types.Header{Number: big.NewInt(4)}) + + _, err = A.AddBlock(a0) + require.NoError(t, err) + _, err = A.AddBlock(a1) + require.NoError(t, err) + + _, err = B.AddBlock(b2) + require.NoError(t, err) + _, err = B.AddBlock(b3) + require.NoError(t, err) + + _, err = C.AddBlock(c4) + require.NoError(t, err) + + m.pruneChannels(eth.L2BlockRef{ + Number: uint64(3), + }) - // Call the function we are testing - m.Requeue(m.defaultCfg) + require.Equal(t, []*channel{C}, m.channelQueue) + + m.pruneChannels(eth.L2BlockRef{ + Number: uint64(4), + }) + + require.Equal(t, []*channel{}, m.channelQueue) + + m.pruneChannels(eth.L2BlockRef{ + Number: uint64(4), + }) - // The requeue shouldn't affect the pending channel - require.Contains(t, m.channelQueue, channel0) + require.Equal(t, []*channel{}, m.channelQueue) - require.NotContains(t, m.blocks, blockA) } func TestChannelManager_ChannelOutFactory(t *testing.T) { type ChannelOutWrapper struct { diff --git a/op-batcher/batcher/channel_test.go b/op-batcher/batcher/channel_test.go index 0aad780131c7..b36ce9311bce 100644 --- a/op-batcher/batcher/channel_test.go +++ b/op-batcher/batcher/channel_test.go @@ -113,7 +113,7 @@ func TestChannelManager_NextTxData(t *testing.T) { frameNumber: uint16(0), }, } - channel.channelBuilder.PushFrames(frame) + channel.channelBuilder.frames = append(channel.channelBuilder.frames, frame) require.Equal(t, 1, channel.PendingFrames()) // Now the nextTxData function should return the frame @@ -142,7 +142,7 @@ func TestChannel_NextTxData_singleFrameTx(t *testing.T) { mockframes := makeMockFrameDatas(chID, n+1) // put multiple frames into channel, but less than target - ch.channelBuilder.PushFrames(mockframes[:n-1]...) + ch.channelBuilder.frames = mockframes[:n-1] requireTxData := func(i int) { require.True(ch.HasTxData(), "expected tx data %d", i) @@ -160,7 +160,7 @@ func TestChannel_NextTxData_singleFrameTx(t *testing.T) { require.False(ch.HasTxData()) // put in last two - ch.channelBuilder.PushFrames(mockframes[n-1 : n+1]...) + ch.channelBuilder.frames = append(ch.channelBuilder.frames, mockframes[n-1:n+1]...) for i := n - 1; i < n+1; i++ { requireTxData(i) } @@ -183,11 +183,11 @@ func TestChannel_NextTxData_multiFrameTx(t *testing.T) { mockframes := makeMockFrameDatas(chID, n+1) // put multiple frames into channel, but less than target - ch.channelBuilder.PushFrames(mockframes[:n-1]...) + ch.channelBuilder.frames = append(ch.channelBuilder.frames, mockframes[:n-1]...) require.False(ch.HasTxData()) // put in last two - ch.channelBuilder.PushFrames(mockframes[n-1 : n+1]...) + ch.channelBuilder.frames = append(ch.channelBuilder.frames, mockframes[n-1:n+1]...) require.True(ch.HasTxData()) txdata := ch.NextTxData() require.Len(txdata.frames, n) @@ -240,7 +240,8 @@ func TestChannelTxConfirmed(t *testing.T) { frameNumber: uint16(0), }, } - m.currentChannel.channelBuilder.PushFrames(frame) + m.currentChannel.channelBuilder.frames = append(m.currentChannel.channelBuilder.frames, frame) + require.Equal(t, 1, m.currentChannel.PendingFrames()) returnedTxData, err := m.nextTxData(m.currentChannel) expectedTxData := singleFrameTxData(frame) @@ -291,7 +292,7 @@ func TestChannelTxFailed(t *testing.T) { frameNumber: uint16(0), }, } - m.currentChannel.channelBuilder.PushFrames(frame) + m.currentChannel.channelBuilder.frames = append(m.currentChannel.channelBuilder.frames, frame) require.Equal(t, 1, m.currentChannel.PendingFrames()) returnedTxData, err := m.nextTxData(m.currentChannel) expectedTxData := singleFrameTxData(frame) diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 6e237b176d8d..2c9db3821e3a 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "math" "math/big" _ "net/http/pprof" "sync" @@ -241,11 +240,12 @@ func (l *BatchSubmitter) StopBatchSubmitting(ctx context.Context) error { // 2. Check if the sync status is valid or if we are all the way up to date // 3. Check if it needs to initialize state OR it is lagging (todo: lagging just means race condition?) // 4. Load all new blocks into the local state. +// 5. Dequeue blocks from local state which are now safe. // // If there is a reorg, it will reset the last stored block but not clear the internal state so // the state can be flushed to L1. -func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) error { - start, end, err := l.calculateL2BlockRangeToStore(ctx) +func (l *BatchSubmitter) loadBlocksIntoState(syncStatus eth.SyncStatus, ctx context.Context) error { + start, end, err := l.calculateL2BlockRangeToStore(syncStatus) if err != nil { l.Log.Warn("Error calculating L2 block range", "err", err) return err @@ -308,12 +308,10 @@ func (l *BatchSubmitter) loadBlockIntoState(ctx context.Context, blockNumber uin return block, nil } -// calculateL2BlockRangeToStore determines the range (start,end] that should be loaded into the local state. -// It also takes care of initializing some local state (i.e. will modify l.lastStoredBlock in certain conditions) -func (l *BatchSubmitter) calculateL2BlockRangeToStore(ctx context.Context) (eth.BlockID, eth.BlockID, error) { +func (l *BatchSubmitter) getSyncStatus(ctx context.Context) (*eth.SyncStatus, error) { rollupClient, err := l.EndpointProvider.RollupClient(ctx) if err != nil { - return eth.BlockID{}, eth.BlockID{}, fmt.Errorf("getting rollup client: %w", err) + return nil, fmt.Errorf("getting rollup client: %w", err) } var ( @@ -331,7 +329,7 @@ func (l *BatchSubmitter) calculateL2BlockRangeToStore(ctx context.Context) (eth. // Ensure that we have the sync status if err != nil { - return eth.BlockID{}, eth.BlockID{}, fmt.Errorf("failed to get sync status: %w", err) + return nil, fmt.Errorf("failed to get sync status: %w", err) } // If we have a head, break out of the loop @@ -348,10 +346,21 @@ func (l *BatchSubmitter) calculateL2BlockRangeToStore(ctx context.Context) (eth. // Reset timer to tick of the new backoff time again timer.Reset(backoff) case <-ctx.Done(): - return eth.BlockID{}, eth.BlockID{}, ctx.Err() + return nil, ctx.Err() } } + return syncStatus, nil +} + +// calculateL2BlockRangeToStore determines the range (start,end] that should be loaded into the local state. +// It also takes care of initializing some local state (i.e. will modify l.lastStoredBlock in certain conditions +// as well as garbage collecting blocks which became safe) +func (l *BatchSubmitter) calculateL2BlockRangeToStore(syncStatus eth.SyncStatus) (eth.BlockID, eth.BlockID, error) { + if syncStatus.HeadL1 == (eth.L1BlockRef{}) { + return eth.BlockID{}, eth.BlockID{}, errors.New("empty sync status") + } + // Check last stored to see if it needs to be set on startup OR set if is lagged behind. // It lagging implies that the op-node processed some batches that were submitted prior to the current instance of the batcher being alive. if l.lastStoredBlock == (eth.BlockID{}) { @@ -430,65 +439,36 @@ func (l *BatchSubmitter) loop() { ticker := time.NewTicker(l.Config.PollInterval) defer ticker.Stop() - publishAndWait := func() { - l.publishStateToL1(queue, receiptsCh, daGroup, time.Duration(math.MaxInt64)) - if !l.Txmgr.IsClosed() { - if l.Config.UseAltDA { - l.Log.Info("Waiting for altDA writes to complete...") - err := daGroup.Wait() - if err != nil { - l.Log.Error("Error returned by one of the altda goroutines waited on", "err", err) - } - } - l.Log.Info("Waiting for L1 txs to be confirmed...") - err := queue.Wait() - if err != nil { - l.Log.Error("Error returned by one of the txmgr goroutines waited on", "err", err) - } - } else { - l.Log.Info("Txmgr is closed, remaining channel data won't be sent") - } - } - for { select { case <-ticker.C: + if !l.checkTxpool(queue, receiptsCh) { continue } - if err := l.loadBlocksIntoState(l.shutdownCtx); errors.Is(err, ErrReorg) { - err := l.state.Close() + + syncStatus, err := l.getSyncStatus(l.shutdownCtx) + if err != nil { + l.Log.Warn("could not get sync status", "err", err) + continue + } + + l.state.pruneSafeBlocks(syncStatus.SafeL2) + l.state.pruneChannels(syncStatus.SafeL2) + if err := l.loadBlocksIntoState(*syncStatus, l.shutdownCtx); errors.Is(err, ErrReorg) { + // Wait for any in flight transactions + // to be ingested by the node before + // we start loading blocks again. + err := l.waitNodeSync() if err != nil { - if errors.Is(err, ErrPendingAfterClose) { - l.Log.Warn("Closed channel manager to handle L2 reorg with pending channel(s) remaining - submitting") - } else { - l.Log.Error("Error closing the channel manager to handle a L2 reorg", "err", err) - } + l.Log.Warn("error waiting for node sync", "err", err) } - // on reorg we want to publish all pending state then wait until each result clears before resetting - // the state. - publishAndWait() l.clearState(l.shutdownCtx) continue } l.publishStateToL1(queue, receiptsCh, daGroup, l.Config.PollInterval) case <-l.shutdownCtx.Done(): - if l.Txmgr.IsClosed() { - l.Log.Info("Txmgr is closed, remaining channel data won't be sent") - return - } - // This removes any never-submitted pending channels, so these do not have to be drained with transactions. - // Any remaining unfinished channel is terminated, so its data gets submitted. - err := l.state.Close() - if err != nil { - if errors.Is(err, ErrPendingAfterClose) { - l.Log.Warn("Closed channel manager on shutdown with pending channel(s) remaining - submitting") - } else { - l.Log.Error("Error closing the channel manager on shutdown", "err", err) - } - } - publishAndWait() - l.Log.Info("Finished publishing all remaining channel data") + l.Log.Warn("main loop returning") return } } diff --git a/op-batcher/metrics/test.go b/op-batcher/metrics/test.go new file mode 100644 index 000000000000..76c365ea7e2b --- /dev/null +++ b/op-batcher/metrics/test.go @@ -0,0 +1,22 @@ +package metrics + +import ( + "github.com/ethereum/go-ethereum/core/types" +) + +type TestMetrics struct { + noopMetrics + PendingBlocksBytesCurrent float64 +} + +var _ Metricer = new(TestMetrics) + +func (m *TestMetrics) RecordL2BlockInPendingQueue(block *types.Block) { + _, rawSize := estimateBatchSize(block) + m.PendingBlocksBytesCurrent += float64(rawSize) + +} +func (m *TestMetrics) RecordL2BlockInChannel(block *types.Block) { + _, rawSize := estimateBatchSize(block) + m.PendingBlocksBytesCurrent -= float64(rawSize) +} diff --git a/op-batcher/readme.md b/op-batcher/readme.md new file mode 100644 index 000000000000..1d601fea94be --- /dev/null +++ b/op-batcher/readme.md @@ -0,0 +1,69 @@ +# op-batcher + +The `op-batcher` is responsible for ensuring data availability. See the [specs](https://specs.optimism.io/protocol/batcher.html). + + +## Interactions & Dependencies +The `op-batcher` works together with the [sequencer](../op-node/) (which it reads unsafe blocks from), the data availability layer (e.g. Layer 1 or an [Alt DA](../op-alt-da/) layer, which it posts data to), and the [derivation pipeline](../op-node/) (which reads the data from the DA layer and progresses the safe chain). + +It depends directly on some code shared with the derivation pipeline, namely the [`ChannelOut`](../op-node/rollup/derive/channel_out.go) implementation(s). It also depends directly on the shared [txmgr](../op-service/txmgr/) module. + +## Testing +The batcher has a suite of unit test which can be triggered by running +``` +go test ./... +``` +from this directory. There are also end-to-end tests in [`op-e2e`](../op-e2e/) which integrate the batcher. + +## Architecture + +The architecture of this batcher implementation is shown on the left side of the following diagram: + +![architecture](./architecture.png) + +Batch submitting (writing to the DA layer, in the middle of the diagram) works together with the derivation pipeline (on the right side of the diagram, reading from the DA layer) to progress the safe chain. + +The philosophy behind the current architecture is: +* Blocks, channels and frames are kept around for as long as they might be needed, and discarded as soon as they are not needed. They are not moved from one part of state to another. +* We retain block data in a strict order for as long as necessary. We only garbage collect frames, channels and blocks when the safe head moves sufficiently and those structures have done their job. +* When something goes wrong, we rewind the state cursors by the minimal amount we need to get going again. + + +### Happy path + +In the happy path, the batcher periodically: +1. Enqueues unsafe blocks and dequeues safe blocks from the sequencer to its internal state. +2. Enqueues a new channel, if necessary. +3. Processes some unprocessed blocks into the current channel, triggers the compression of the block data and the creation of frames. +4. Sends frames from the channel queue to the DA layer as (e.g. to Ethereum L1 as calldata or blob transactions). +5. If there is more transaction data to send, go to 2. Else wait for a tick and go to 1. + + +The `blockCursor` state variable tracks the next unprocessed block. +In each channel, the `frameCursor` tracks the next unsent frame. + + +### Reorgs +When an L2 unsafe reorg is detected, the batch submitter will reset its state, and wait for any in flight transactions to be ingested by the verifier nodes before starting work again. + +### Tx Failed +When a Tx fails, an asynchronous receipts handler is triggered. The channel from whence the Tx's frames came has its `frameCursor` rewound, so that all the frames can be resubmitted in order. + +### Channel Times Out +When at Tx is confirmed, an asynchronous receipts handler is triggered. We only update the batcher's state if the channel timed out on chain. In that case, the `blockCursor` is rewound to the first block added to that channel, and the channel queue is cleared out. This allows the batcher to start fresh building a new channel starting from the same block -- it does not need to refetch blocks from the sequencer. + +## Design Principles and Optimization Targets +At the current time, the batcher should be optimized for correctness, simplicity and robustness. It is considered preferable to prioritize these properties, even at the expense of other potentially desirable properties such as frugality. For example, it is preferable to have the batcher resubmit some data from time to time ("wasting" money on data availability costs) instead of avoiding that by e.g. adding some persistent state to the batcher. + +The batcher can almost always recover from unforeseen situations by being restarted. + + +Some complexity is permitted, however, for handling data availability switching, so that the batcher is not wasting money for longer periods of time. + +## Known issues and future work + +Link to [open issues with the `op-batcher` tag](https://github.com/ethereum-optimism/optimism/issues?q=is%3Aopen+is%3Aissue+label%3AA-op-batcher). + +The batcher launches L1 transactions in parallel so that it can achieve higher throughput, particularly in situations where there is a large backlog of data which needs to be posted. Sometimes, transactions can get stuck in the L1 mempool. The batcher does have functionality to clear these stuck transactions, but it is not completely reliable. + +The automatic data availability switching behavior is a somewhat new feature which may still have some bugs in it. From 01559d2ea3242f200adad77dec8797120ffe929d Mon Sep 17 00:00:00 2001 From: soyboy <85043086+sbvegan@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:38:12 +0800 Subject: [PATCH 194/451] adding conduit bootnodes (#12534) * removing duplicate default bootnode * adding conduit bootnodes * removing duplicate bootnode --- op-node/p2p/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/op-node/p2p/config.go b/op-node/p2p/config.go index ee21ba20fc39..10a75881b87d 100644 --- a/op-node/p2p/config.go +++ b/op-node/p2p/config.go @@ -29,7 +29,6 @@ var DefaultBootnodes = []*enode.Node{ // OP Labs enode.MustParse("enode://869d07b5932f17e8490990f75a3f94195e9504ddb6b85f7189e5a9c0a8fff8b00aecf6f3ac450ecba6cdabdb5858788a94bde2b613e0f2d82e9b395355f76d1a@34.65.67.101:30305"), enode.MustParse("enode://2d4e7e9d48f4dd4efe9342706dd1b0024681bd4c3300d021f86fc75eab7865d4e0cbec6fbc883f011cfd6a57423e7e2f6e104baad2b744c3cafaec6bc7dc92c1@34.65.43.171:30305"), - enode.MustParse("enode://9d7a3efefe442351217e73b3a593bcb8efffb55b4807699972145324eab5e6b382152f8d24f6301baebbfb5ecd4127bd3faab2842c04cd432bdf50ba092f6645@34.65.109.126:30305"), // Base enode.MustParse("enr:-J24QNz9lbrKbN4iSmmjtnr7SjUMk4zB7f1krHZcTZx-JRKZd0kA2gjufUROD6T3sOWDVDnFJRvqBBo62zuF-hYCohOGAYiOoEyEgmlkgnY0gmlwhAPniryHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQKNVFlCxh_B-716tTs-h1vMzZkSs1FTu_OYTNjgufplG4N0Y3CCJAaDdWRwgiQG"), enode.MustParse("enr:-J24QH-f1wt99sfpHy4c0QJM-NfmsIfmlLAMMcgZCUEgKG_BBYFc6FwYgaMJMQN5dsRBJApIok0jFn-9CS842lGpLmqGAYiOoDRAgmlkgnY0gmlwhLhIgb2Hb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJ9FTIv8B9myn1MWaC_2lJ-sMoeCDkusCsk4BYHjjCq04N0Y3CCJAaDdWRwgiQG"), @@ -37,7 +36,9 @@ var DefaultBootnodes = []*enode.Node{ enode.MustParse("enr:-J24QHmGyBwUZXIcsGYMaUqGGSl4CFdx9Tozu-vQCn5bHIQbR7On7dZbU61vYvfrJr30t0iahSqhc64J46MnUO2JvQaGAYiOoCKKgmlkgnY0gmlwhAPnCzSHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQINc4fSijfbNIiGhcgvwjsjxVFJHUstK9L1T8OTKUjgloN0Y3CCJAaDdWRwgiQG"), enode.MustParse("enr:-J24QG3ypT4xSu0gjb5PABCmVxZqBjVw9ca7pvsI8jl4KATYAnxBmfkaIuEqy9sKvDHKuNCsy57WwK9wTt2aQgcaDDyGAYiOoGAXgmlkgnY0gmlwhDbGmZaHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQIeAK_--tcLEiu7HvoUlbV52MspE0uCocsx1f_rYvRenIN0Y3CCJAaDdWRwgiQG"), // Conduit - enode.MustParse("enode://9d7a3efefe442351217e73b3a593bcb8efffb55b4807699972145324eab5e6b382152f8d24f6301baebbfb5ecd4127bd3faab2842c04cd432bdf50ba092f6645@34.65.109.126:30305"), + enode.MustParse("enode://d25ce99435982b04d60c4b41ba256b84b888626db7bee45a9419382300fbe907359ae5ef250346785bff8d3b9d07cd3e017a27e2ee3cfda3bcbb0ba762ac9674@bootnode.conduit.xyz:0?discport=30301"), + enode.MustParse("enode://2d4e7e9d48f4dd4efe9342706dd1b0024681bd4c3300d021f86fc75eab7865d4e0cbec6fbc883f011cfd6a57423e7e2f6e104baad2b744c3cafaec6bc7dc92c1@34.65.43.171:0?discport=30305"), + enode.MustParse("enode://9d7a3efefe442351217e73b3a593bcb8efffb55b4807699972145324eab5e6b382152f8d24f6301baebbfb5ecd4127bd3faab2842c04cd432bdf50ba092f6645@34.65.109.126:0?discport=30305"), } type HostMetrics interface { From fd427154f66938505c9c5e7a28feebadf0002a09 Mon Sep 17 00:00:00 2001 From: Chen Kai <281165273grape@gmail.com> Date: Mon, 18 Nov 2024 23:31:57 +0800 Subject: [PATCH 195/451] cannon: Add more concurrency primitives tests (#12771) * feat:Add more sync tests Signed-off-by: Chen Kai <281165273grape@gmail.com> * feat:add more sync tests Signed-off-by: Chen Kai <281165273grape@gmail.com> * fix:fix failed tests Signed-off-by: Chen Kai <281165273grape@gmail.com> * fix:add copyright credit and remove unused code Signed-off-by: Chen Kai <281165273grape@gmail.com> * fix:fix copyright format Signed-off-by: Chen Kai <281165273grape@gmail.com> * fix code review suggestions Signed-off-by: Chen Kai <281165273grape@gmail.com> --------- Signed-off-by: Chen Kai <281165273grape@gmail.com> --- .../multithreaded/instrumented_test.go | 99 ++++-- .../example/{multithreaded => mt-cond}/go.mod | 2 +- cannon/testdata/example/mt-cond/main.go | 302 ++++++++++++++++++ cannon/testdata/example/mt-map/go.mod | 5 + cannon/testdata/example/mt-map/main.go | 43 +++ cannon/testdata/example/mt-mutex/go.mod | 5 + cannon/testdata/example/mt-mutex/main.go | 82 +++++ cannon/testdata/example/mt-once/go.mod | 5 + cannon/testdata/example/mt-once/main.go | 98 ++++++ cannon/testdata/example/mt-pool/go.mod | 5 + cannon/testdata/example/mt-pool/main.go | 38 +++ cannon/testdata/example/mt-rwmutex/go.mod | 5 + cannon/testdata/example/mt-rwmutex/main.go | 226 +++++++++++++ cannon/testdata/example/mt-wg/go.mod | 5 + .../example/{multithreaded => mt-wg}/main.go | 6 - 15 files changed, 901 insertions(+), 25 deletions(-) rename cannon/testdata/example/{multithreaded => mt-cond}/go.mod (58%) create mode 100644 cannon/testdata/example/mt-cond/main.go create mode 100644 cannon/testdata/example/mt-map/go.mod create mode 100644 cannon/testdata/example/mt-map/main.go create mode 100644 cannon/testdata/example/mt-mutex/go.mod create mode 100644 cannon/testdata/example/mt-mutex/main.go create mode 100644 cannon/testdata/example/mt-once/go.mod create mode 100644 cannon/testdata/example/mt-once/main.go create mode 100644 cannon/testdata/example/mt-pool/go.mod create mode 100644 cannon/testdata/example/mt-pool/main.go create mode 100644 cannon/testdata/example/mt-rwmutex/go.mod create mode 100644 cannon/testdata/example/mt-rwmutex/main.go create mode 100644 cannon/testdata/example/mt-wg/go.mod rename cannon/testdata/example/{multithreaded => mt-wg}/main.go (79%) diff --git a/cannon/mipsevm/multithreaded/instrumented_test.go b/cannon/mipsevm/multithreaded/instrumented_test.go index ec079e50c8ce..6c8fade9ff53 100644 --- a/cannon/mipsevm/multithreaded/instrumented_test.go +++ b/cannon/mipsevm/multithreaded/instrumented_test.go @@ -36,27 +36,90 @@ func TestInstrumentedState_Claim(t *testing.T) { func TestInstrumentedState_MultithreadedProgram(t *testing.T) { t.Parallel() - state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath("multithreaded"), CreateInitialState, false) - oracle := testutil.StaticOracle(t, []byte{}) - - var stdOutBuf, stdErrBuf bytes.Buffer - us := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger(), nil) - for i := 0; i < 2_000_000; i++ { - if us.GetState().GetExited() { - break - } - _, err := us.Step(false) - require.NoError(t, err) + cases := []struct { + name string + expectedOutput []string + programName string + }{ + { + name: "wg and chan test", + expectedOutput: []string{ + "waitgroup result: 42", + "channels result: 1234", + }, + programName: "mt-wg", + }, + { + name: "mutex test", + expectedOutput: []string{ + "Mutex test passed", + }, + programName: "mt-mutex", + }, + { + name: "cond test", + expectedOutput: []string{ + "Cond test passed", + }, + programName: "mt-cond", + }, + { + name: "rwmutex test", + expectedOutput: []string{ + "RWMutex test passed", + }, + programName: "mt-rwmutex", + }, + { + name: "once test", + expectedOutput: []string{ + "Once test passed", + }, + programName: "mt-once", + }, + { + name: "map test", + expectedOutput: []string{ + "Map test passed", + }, + programName: "mt-map", + }, + { + name: "pool test", + expectedOutput: []string{ + "Pool test passed", + }, + programName: "mt-pool", + }, } - t.Logf("Completed in %d steps", state.Step) - require.True(t, state.Exited, "must complete program") - require.Equal(t, uint8(0), state.ExitCode, "exit with 0") - require.Contains(t, "waitgroup result: 42", stdErrBuf.String()) - require.Contains(t, "channels result: 1234", stdErrBuf.String()) - require.Equal(t, "", stdErrBuf.String(), "should not print any errors") -} + for _, test := range cases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath(test.programName), CreateInitialState, false) + oracle := testutil.StaticOracle(t, []byte{}) + var stdOutBuf, stdErrBuf bytes.Buffer + us := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger(), nil) + for i := 0; i < 5_000_000; i++ { + if us.GetState().GetExited() { + break + } + _, err := us.Step(false) + require.NoError(t, err) + } + t.Logf("Completed in %d steps", state.Step) + + require.True(t, state.Exited, "must complete program") + require.Equal(t, uint8(0), state.ExitCode, "exit with 0") + for _, expected := range test.expectedOutput { + require.Contains(t, stdOutBuf.String(), expected) + } + require.Equal(t, "", stdErrBuf.String(), "should not print any errors") + }) + } +} func TestInstrumentedState_Alloc(t *testing.T) { if os.Getenv("SKIP_SLOW_TESTS") == "true" { t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") diff --git a/cannon/testdata/example/multithreaded/go.mod b/cannon/testdata/example/mt-cond/go.mod similarity index 58% rename from cannon/testdata/example/multithreaded/go.mod rename to cannon/testdata/example/mt-cond/go.mod index e1bdb77a9aff..d6d1853d5af2 100644 --- a/cannon/testdata/example/multithreaded/go.mod +++ b/cannon/testdata/example/mt-cond/go.mod @@ -1,4 +1,4 @@ -module multithreaded +module cond go 1.22 diff --git a/cannon/testdata/example/mt-cond/main.go b/cannon/testdata/example/mt-cond/main.go new file mode 100644 index 000000000000..2b584cec999b --- /dev/null +++ b/cannon/testdata/example/mt-cond/main.go @@ -0,0 +1,302 @@ +// Portions of this code are derived from code written by The Go Authors. +// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/cond_test.go +// +// --- Original License Notice --- +// +// Copyright 2009 The Go Authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package main + +import ( + "fmt" + "os" + "reflect" + "runtime" + "sync" +) + +func main() { + TestCondSignal() + TestCondSignalGenerations() + TestCondBroadcast() + TestRace() + TestCondSignalStealing() + TestCondCopy() + + fmt.Println("Cond test passed") +} + +func TestCondSignal() { + var m sync.Mutex + c := sync.NewCond(&m) + n := 2 + running := make(chan bool, n) + awake := make(chan bool, n) + for i := 0; i < n; i++ { + go func() { + m.Lock() + running <- true + c.Wait() + awake <- true + m.Unlock() + }() + } + for i := 0; i < n; i++ { + <-running // Wait for everyone to run. + } + for n > 0 { + select { + case <-awake: + _, _ = fmt.Fprintln(os.Stderr, "goroutine not asleep") + os.Exit(1) + default: + } + m.Lock() + c.Signal() + m.Unlock() + <-awake // Will deadlock if no goroutine wakes up + select { + case <-awake: + _, _ = fmt.Fprintln(os.Stderr, "too many goroutines awake") + os.Exit(1) + default: + } + n-- + } + c.Signal() +} + +func TestCondSignalGenerations() { + var m sync.Mutex + c := sync.NewCond(&m) + n := 100 + running := make(chan bool, n) + awake := make(chan int, n) + for i := 0; i < n; i++ { + go func(i int) { + m.Lock() + running <- true + c.Wait() + awake <- i + m.Unlock() + }(i) + if i > 0 { + a := <-awake + if a != i-1 { + _, _ = fmt.Fprintf(os.Stderr, "wrong goroutine woke up: want %d, got %d\n", i-1, a) + os.Exit(1) + } + } + <-running + m.Lock() + c.Signal() + m.Unlock() + } +} + +func TestCondBroadcast() { + var m sync.Mutex + c := sync.NewCond(&m) + n := 5 + running := make(chan int, n) + awake := make(chan int, n) + exit := false + for i := 0; i < n; i++ { + go func(g int) { + m.Lock() + for !exit { + running <- g + c.Wait() + awake <- g + } + m.Unlock() + }(i) + } + for i := 0; i < n; i++ { + for i := 0; i < n; i++ { + <-running // Will deadlock unless n are running. + } + if i == n-1 { + m.Lock() + exit = true + m.Unlock() + } + select { + case <-awake: + _, _ = fmt.Fprintln(os.Stderr, "goroutine not asleep") + os.Exit(1) + default: + } + m.Lock() + c.Broadcast() + m.Unlock() + seen := make([]bool, n) + for i := 0; i < n; i++ { + g := <-awake + if seen[g] { + _, _ = fmt.Fprintln(os.Stderr, "goroutine woke up twice") + os.Exit(1) + } + seen[g] = true + } + } + select { + case <-running: + _, _ = fmt.Fprintln(os.Stderr, "goroutine still running") + os.Exit(1) + default: + } + c.Broadcast() +} + +func TestRace() { + x := 0 + c := sync.NewCond(&sync.Mutex{}) + done := make(chan bool) + go func() { + c.L.Lock() + x = 1 + c.Wait() + if x != 2 { + _, _ = fmt.Fprintln(os.Stderr, "want 2") + os.Exit(1) + } + x = 3 + c.Signal() + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 1 { + x = 2 + c.Signal() + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 2 { + c.Wait() + if x != 3 { + _, _ = fmt.Fprintln(os.Stderr, "want 3") + os.Exit(1) + } + break + } + if x == 3 { + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + <-done + <-done + <-done +} + +func TestCondSignalStealing() { + for iters := 0; iters < 5; iters++ { + var m sync.Mutex + cond := sync.NewCond(&m) + + // Start a waiter. + ch := make(chan struct{}) + go func() { + m.Lock() + ch <- struct{}{} + cond.Wait() + m.Unlock() + + ch <- struct{}{} + }() + + <-ch + m.Lock() + m.Unlock() + + // We know that the waiter is in the cond.Wait() call because we + // synchronized with it, then acquired/released the mutex it was + // holding when we synchronized. + // + // Start two goroutines that will race: one will broadcast on + // the cond var, the other will wait on it. + // + // The new waiter may or may not get notified, but the first one + // has to be notified. + done := false + go func() { + cond.Broadcast() + }() + + go func() { + m.Lock() + for !done { + cond.Wait() + } + m.Unlock() + }() + + // Check that the first waiter does get signaled. + <-ch + + // Release the second waiter in case it didn't get the + // broadcast. + m.Lock() + done = true + m.Unlock() + cond.Broadcast() + } +} + +func TestCondCopy() { + defer func() { + err := recover() + if err == nil || err.(string) != "sync.Cond is copied" { + _, _ = fmt.Fprintf(os.Stderr, "got %v, expect sync.Cond is copied", err) + os.Exit(1) + } + }() + c := sync.Cond{L: &sync.Mutex{}} + c.Signal() + var c2 sync.Cond + reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet + c2.Signal() +} diff --git a/cannon/testdata/example/mt-map/go.mod b/cannon/testdata/example/mt-map/go.mod new file mode 100644 index 000000000000..1d7b890f7f1a --- /dev/null +++ b/cannon/testdata/example/mt-map/go.mod @@ -0,0 +1,5 @@ +module map + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/mt-map/main.go b/cannon/testdata/example/mt-map/main.go new file mode 100644 index 000000000000..085319b88d87 --- /dev/null +++ b/cannon/testdata/example/mt-map/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "sync" +) + +func main() { + var m sync.Map + + m.Store("hello", "world") + m.Store("foo", "bar") + m.Store("baz", "qux") + + m.Delete("foo") + m.Load("baz") + + go func() { + m.CompareAndDelete("hello", "world") + m.LoadAndDelete("baz") + }() + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + m.Load("hello") + m.Load("baz") + m.Range(func(k, v interface{}) bool { + m.Load("hello") + m.Load("baz") + return true + }) + m.CompareAndSwap("hello", "world", "Go") + m.LoadOrStore("hello", "world") + wg.Done() + }() + } + + wg.Wait() + + fmt.Println("Map test passed") +} diff --git a/cannon/testdata/example/mt-mutex/go.mod b/cannon/testdata/example/mt-mutex/go.mod new file mode 100644 index 000000000000..368cf9be0ae5 --- /dev/null +++ b/cannon/testdata/example/mt-mutex/go.mod @@ -0,0 +1,5 @@ +module mutex + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/mt-mutex/main.go b/cannon/testdata/example/mt-mutex/main.go new file mode 100644 index 000000000000..4f96e5d2308d --- /dev/null +++ b/cannon/testdata/example/mt-mutex/main.go @@ -0,0 +1,82 @@ +// Portions of this code are derived from code written by The Go Authors. +// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/mutex_test.go +// +// --- Original License Notice --- +// +// Copyright 2009 The Go Authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package main + +import ( + "fmt" + "os" + "sync" +) + +func main() { + TestMutex() +} + +func TestMutex() { + m := new(sync.Mutex) + + m.Lock() + if m.TryLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex locked") + os.Exit(1) + } + m.Unlock() + if !m.TryLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryLock failed with mutex unlocked") + os.Exit(1) + } + m.Unlock() + + c := make(chan bool) + for i := 0; i < 10; i++ { + go HammerMutex(m, 1000, c) + } + for i := 0; i < 10; i++ { + <-c + } + fmt.Println("Mutex test passed") +} + +func HammerMutex(m *sync.Mutex, loops int, cdone chan bool) { + for i := 0; i < loops; i++ { + if i%3 == 0 { + if m.TryLock() { + m.Unlock() + } + continue + } + m.Lock() + m.Unlock() + } + cdone <- true +} diff --git a/cannon/testdata/example/mt-once/go.mod b/cannon/testdata/example/mt-once/go.mod new file mode 100644 index 000000000000..7595e1de483f --- /dev/null +++ b/cannon/testdata/example/mt-once/go.mod @@ -0,0 +1,5 @@ +module once + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/mt-once/main.go b/cannon/testdata/example/mt-once/main.go new file mode 100644 index 000000000000..3be753e2f702 --- /dev/null +++ b/cannon/testdata/example/mt-once/main.go @@ -0,0 +1,98 @@ +// Portions of this code are derived from code written by The Go Authors. +// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/once_test.go +// +// --- Original License Notice --- +// +// Copyright 2009 The Go Authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package main + +import ( + "fmt" + "os" + "sync" +) + +func main() { + TestOnce() + TestOncePanic() + + fmt.Println("Once test passed") +} + +type one int + +func (o *one) Increment() { + *o++ +} + +func run(once *sync.Once, o *one, c chan bool) { + once.Do(func() { o.Increment() }) + if v := *o; v != 1 { + _, _ = fmt.Fprintf(os.Stderr, "once failed inside run: %d is not 1\n", v) + os.Exit(1) + } + c <- true +} + +func TestOnce() { + o := new(one) + once := new(sync.Once) + c := make(chan bool) + const N = 10 + for i := 0; i < N; i++ { + go run(once, o, c) + } + for i := 0; i < N; i++ { + <-c + } + if *o != 1 { + _, _ = fmt.Fprintf(os.Stderr, "once failed outside run: %d is not 1\n", *o) + os.Exit(1) + } +} + +func TestOncePanic() { + var once sync.Once + func() { + defer func() { + if r := recover(); r == nil { + _, _ = fmt.Fprintf(os.Stderr, "Once.Do did not panic") + os.Exit(1) + } + }() + once.Do(func() { + panic("failed") + }) + }() + + once.Do(func() { + _, _ = fmt.Fprintf(os.Stderr, "Once.Do called twice") + os.Exit(1) + }) +} diff --git a/cannon/testdata/example/mt-pool/go.mod b/cannon/testdata/example/mt-pool/go.mod new file mode 100644 index 000000000000..f57bdf9b81e0 --- /dev/null +++ b/cannon/testdata/example/mt-pool/go.mod @@ -0,0 +1,5 @@ +module pool + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/mt-pool/main.go b/cannon/testdata/example/mt-pool/main.go new file mode 100644 index 000000000000..267710f3ea3a --- /dev/null +++ b/cannon/testdata/example/mt-pool/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "sync" +) + +func main() { + var x sync.Pool + + x.Put(1) + x.Put(2) + + // try some concurrency! + var wg sync.WaitGroup + wg.Add(2) + go func() { + x.Put(3) + wg.Done() + }() + go func() { + x.Put(4) + wg.Done() + }() + + wg.Wait() + + wg.Add(4) + for i := 0; i < 4; i++ { + go func() { + x.Get() + wg.Done() + }() + } + wg.Wait() + + fmt.Println("Pool test passed") +} diff --git a/cannon/testdata/example/mt-rwmutex/go.mod b/cannon/testdata/example/mt-rwmutex/go.mod new file mode 100644 index 000000000000..a0a433e91199 --- /dev/null +++ b/cannon/testdata/example/mt-rwmutex/go.mod @@ -0,0 +1,5 @@ +module rwmutex + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/mt-rwmutex/main.go b/cannon/testdata/example/mt-rwmutex/main.go new file mode 100644 index 000000000000..8553bba75ef4 --- /dev/null +++ b/cannon/testdata/example/mt-rwmutex/main.go @@ -0,0 +1,226 @@ +// Portions of this code are derived from code written by The Go Authors. +// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/rwmutex_test.go +// +// --- Original License Notice --- +// +// Copyright 2009 The Go Authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package main + +import ( + "fmt" + "os" + "runtime" + "sync" + "sync/atomic" +) + +func main() { + TestParallelReaders() + TestRLocker() + TestRWMutex() + + fmt.Println("RWMutex test passed") +} + +func parallelReader(m *sync.RWMutex, clocked, cunlock, cdone chan bool) { + m.RLock() + clocked <- true + <-cunlock + m.RUnlock() + cdone <- true +} + +func doTestParallelReaders(numReaders, gomaxprocs int) { + runtime.GOMAXPROCS(gomaxprocs) + var m sync.RWMutex + clocked := make(chan bool) + cunlock := make(chan bool) + cdone := make(chan bool) + for i := 0; i < numReaders; i++ { + go parallelReader(&m, clocked, cunlock, cdone) + } + // Wait for all parallel RLock()s to succeed. + for i := 0; i < numReaders; i++ { + <-clocked + } + for i := 0; i < numReaders; i++ { + cunlock <- true + } + // Wait for the goroutines to finish. + for i := 0; i < numReaders; i++ { + <-cdone + } +} + +func TestParallelReaders() { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) + doTestParallelReaders(1, 4) + doTestParallelReaders(3, 4) + doTestParallelReaders(4, 2) +} + +func reader(rwm *sync.RWMutex, num_iterations int, activity *int32, cdone chan bool) { + for i := 0; i < num_iterations; i++ { + rwm.RLock() + n := atomic.AddInt32(activity, 1) + if n < 1 || n >= 10000 { + rwm.RUnlock() + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + atomic.AddInt32(activity, -1) + rwm.RUnlock() + } + cdone <- true +} + +func writer(rwm *sync.RWMutex, num_iterations int, activity *int32, cdone chan bool) { + for i := 0; i < num_iterations; i++ { + rwm.Lock() + n := atomic.AddInt32(activity, 10000) + if n != 10000 { + rwm.Unlock() + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + atomic.AddInt32(activity, -10000) + rwm.Unlock() + } + cdone <- true +} + +func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { + runtime.GOMAXPROCS(gomaxprocs) + // Number of active readers + 10000 * number of active writers. + var activity int32 + var rwm sync.RWMutex + cdone := make(chan bool) + go writer(&rwm, num_iterations, &activity, cdone) + var i int + for i = 0; i < numReaders/2; i++ { + go reader(&rwm, num_iterations, &activity, cdone) + } + go writer(&rwm, num_iterations, &activity, cdone) + for ; i < numReaders; i++ { + go reader(&rwm, num_iterations, &activity, cdone) + } + // Wait for the 2 writers and all readers to finish. + for i := 0; i < 2+numReaders; i++ { + <-cdone + } +} + +func TestRWMutex() { + var m sync.RWMutex + + m.Lock() + if m.TryLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex locked") + os.Exit(1) + } + if m.TryRLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryRLock succeeded with mutex locked") + os.Exit(1) + } + m.Unlock() + + if !m.TryLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryLock failed with mutex unlocked") + os.Exit(1) + } + m.Unlock() + + if !m.TryRLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryRLock failed with mutex unlocked") + os.Exit(1) + } + if !m.TryRLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryRLock failed with mutex unlocked") + os.Exit(1) + } + if m.TryLock() { + _, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex rlocked") + os.Exit(1) + } + m.RUnlock() + m.RUnlock() + + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) + n := 5 + + HammerRWMutex(1, 1, n) + HammerRWMutex(1, 3, n) + HammerRWMutex(1, 10, n) + HammerRWMutex(4, 1, n) + HammerRWMutex(4, 3, n) + HammerRWMutex(4, 10, n) + HammerRWMutex(10, 1, n) + HammerRWMutex(10, 3, n) + HammerRWMutex(10, 10, n) + HammerRWMutex(10, 5, n) +} + +func TestRLocker() { + var wl sync.RWMutex + var rl sync.Locker + wlocked := make(chan bool, 1) + rlocked := make(chan bool, 1) + rl = wl.RLocker() + n := 10 + go func() { + for i := 0; i < n; i++ { + rl.Lock() + rl.Lock() + rlocked <- true + wl.Lock() + wlocked <- true + } + }() + for i := 0; i < n; i++ { + <-rlocked + rl.Unlock() + select { + case <-wlocked: + _, _ = fmt.Fprintln(os.Stderr, "RLocker() didn't read-lock it") + os.Exit(1) + default: + } + rl.Unlock() + <-wlocked + select { + case <-rlocked: + _, _ = fmt.Fprintln(os.Stderr, "RLocker() didn't respect the write lock") + os.Exit(1) + default: + } + wl.Unlock() + } +} diff --git a/cannon/testdata/example/mt-wg/go.mod b/cannon/testdata/example/mt-wg/go.mod new file mode 100644 index 000000000000..c8a518a535b5 --- /dev/null +++ b/cannon/testdata/example/mt-wg/go.mod @@ -0,0 +1,5 @@ +module wg + +go 1.22 + +toolchain go1.22.0 diff --git a/cannon/testdata/example/multithreaded/main.go b/cannon/testdata/example/mt-wg/main.go similarity index 79% rename from cannon/testdata/example/multithreaded/main.go rename to cannon/testdata/example/mt-wg/main.go index bc871f56e524..841125c6b925 100644 --- a/cannon/testdata/example/multithreaded/main.go +++ b/cannon/testdata/example/mt-wg/main.go @@ -2,8 +2,6 @@ package main import ( "fmt" - "os" - "runtime" "sync" "sync/atomic" ) @@ -39,8 +37,4 @@ func main() { a <- 1234 out := <-c fmt.Printf("channels result: %d\n", out) - - // try a GC! (the runtime might not have run one yet) - runtime.GC() - _, _ = os.Stdout.Write([]byte("GC complete!\n")) } From bc6f8de543608be28590949259155f5ad163c986 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:45:44 +0800 Subject: [PATCH 196/451] dependabot(gomod): bump golang.org/x/sync from 0.8.0 to 0.9.0 (#12885) Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/sync/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d1be77ddc0e1..11a236812990 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/urfave/cli/v2 v2.27.5 golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c - golang.org/x/sync v0.8.0 + golang.org/x/sync v0.9.0 golang.org/x/term v0.25.0 golang.org/x/time v0.7.0 ) diff --git a/go.sum b/go.sum index 5eabf529b0df..8436ca18ce06 100644 --- a/go.sum +++ b/go.sum @@ -927,8 +927,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 29f76e54dc6a60094f5a5511f4c6a3f398a34240 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Mon, 18 Nov 2024 18:24:11 +0100 Subject: [PATCH 197/451] Sc/fix update semgrep (#12942) * fix upgrade-semgrep * fix upgrade-semgrep --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index e40186f75dff..6438ba36197a 100644 --- a/justfile +++ b/justfile @@ -61,4 +61,4 @@ check-semgrep: [ "$(just print-semgrep)" = "$(jq -r .semgrep < versions.json)" ] && echo '✓ semgrep versions match' || (echo '✗ semgrep version mismatch. Run `just upgrade-semgrep` to upgrade.' && exit 1) upgrade-semgrep: - jq '.semgrep = $v' --arg v $(just print-semgrep) <<<$(cat versions.json) > versions.json + pip3 install semgrep=="$(jq -r .semgrep < versions.json)" From f6810a40292babf3c35ee2e97c6370a0b9f15a9a Mon Sep 17 00:00:00 2001 From: blaine Date: Mon, 18 Nov 2024 16:58:06 -0500 Subject: [PATCH 198/451] feat: opcm mainnet deploy. (#12953) --- op-deployer/pkg/deployer/standard/standard.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 8c4ade1dcfb3..516bdb650283 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -133,9 +133,8 @@ func CommitForDeployTag(tag string) (string, error) { func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) { switch chainID { case 1: - // Generated using the bootstrap command on 10/18/2024. - // TODO: @blmalone this needs re-bootstrapped because it's still proxied - return common.HexToAddress(""), nil + // Generated using the bootstrap command on 11/18/2024. + return common.HexToAddress("0x9bc0a1ed534bfb31a6be69e5b767cba332f14347"), nil case 11155111: // Generated using the bootstrap command on 11/15/2024. return common.HexToAddress("0xde9eacb994a6eb12997445f8a63a22772c5c4313"), nil From f94151b6c1b13243c3d6e9c3bf8a532287ec0a24 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 18 Nov 2024 14:59:13 -0700 Subject: [PATCH 199/451] op-deployer: Refactor semver inspector, add L2 genesis test (#12946) - Refactor the semver inspector so that it can be called from outside the CLI - Adds a a canonical L2 genesis file for v1.6.0, and a unit test to assert that new chains deployed using v1.6.0 have the right L2 genesis. --- op-deployer/pkg/deployer/artifacts/locator.go | 16 ++ op-deployer/pkg/deployer/inspect/semvers.go | 164 ++++++++++++------ .../deployer/integration_test/apply_test.go | 134 +++++++++++++- .../testdata/allocs-l2-v160.json.gz | Bin 0 -> 152466 bytes 4 files changed, 260 insertions(+), 54 deletions(-) create mode 100644 op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160.json.gz diff --git a/op-deployer/pkg/deployer/artifacts/locator.go b/op-deployer/pkg/deployer/artifacts/locator.go index 160e8790420b..aa44d43644c8 100644 --- a/op-deployer/pkg/deployer/artifacts/locator.go +++ b/op-deployer/pkg/deployer/artifacts/locator.go @@ -24,6 +24,22 @@ var DefaultL2ContractsLocator = &Locator{ Tag: standard.DefaultL2ContractsTag, } +func NewLocatorFromTag(tag string) (*Locator, error) { + loc := new(Locator) + if err := loc.UnmarshalText([]byte("tag://" + tag)); err != nil { + return nil, fmt.Errorf("failed to unmarshal tag: %w", err) + } + return loc, nil +} + +func MustNewLocatorFromTag(tag string) *Locator { + loc, err := NewLocatorFromTag(tag) + if err != nil { + panic(err) + } + return loc +} + type Locator struct { URL *url.URL Tag string diff --git a/op-deployer/pkg/deployer/inspect/semvers.go b/op-deployer/pkg/deployer/inspect/semvers.go index da666096ee18..48e16d21dbc4 100644 --- a/op-deployer/pkg/deployer/inspect/semvers.go +++ b/op-deployer/pkg/deployer/inspect/semvers.go @@ -8,6 +8,10 @@ import ( "regexp" "time" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" @@ -25,8 +29,6 @@ import ( "github.com/urfave/cli/v2" ) -var versionSelector = []byte{0x54, 0xfd, 0x4d, 0x50} - func L2SemversCLI(cliCtx *cli.Context) error { cliCfg, err := readConfig(cliCtx) if err != nil { @@ -67,6 +69,60 @@ func L2SemversCLI(cliCtx *cli.Context) error { } }() + ps, err := L2Semvers(L2SemversConfig{ + Lgr: l, + Artifacts: artifactsFS, + ChainState: chainState, + }) + if err != nil { + return fmt.Errorf("failed to get L2 semvers: %w", err) + } + + if err := jsonutil.WriteJSON(ps, ioutil.ToStdOutOrFileOrNoop(cliCfg.Outfile, 0o666)); err != nil { + return fmt.Errorf("failed to write rollup config: %w", err) + } + + return nil +} + +type L2SemversConfig struct { + Lgr log.Logger + Artifacts foundry.StatDirFs + ChainState *state.ChainState +} + +type L2PredeploySemvers struct { + L2ToL1MessagePasser string + DeployerWhitelist string + WETH string + L2CrossDomainMessenger string + L2StandardBridge string + SequencerFeeVault string + OptimismMintableERC20Factory string + L1BlockNumber string + GasPriceOracle string + L1Block string + LegacyMessagePasser string + L2ERC721Bridge string + OptimismMintableERC721Factory string + BaseFeeVault string + L1FeeVault string + SchemaRegistry string + EAS string + CrossL2Inbox string + L2toL2CrossDomainMessenger string + SuperchainWETH string + ETHLiquidity string + SuperchainTokenBridge string + OptimismMintableERC20 string + OptimismMintableERC721 string +} + +func L2Semvers(cfg L2SemversConfig) (*L2PredeploySemvers, error) { + l := cfg.Lgr + artifactsFS := cfg.Artifacts + chainState := cfg.ChainState + host, err := env.DefaultScriptHost( broadcaster.NoopBroadcaster(), l, @@ -74,85 +130,89 @@ func L2SemversCLI(cliCtx *cli.Context) error { artifactsFS, ) if err != nil { - return fmt.Errorf("failed to create script host: %w", err) + return nil, fmt.Errorf("failed to create script host: %w", err) } host.ImportState(chainState.Allocs.Data) - addr := common.Address{19: 0x01} - type contractToCheck struct { - Address common.Address - Name string + Address common.Address + FieldPtr *string + Name string } - contractsOutput := make(map[string]string) + var ps L2PredeploySemvers - // The gov token and the proxy admin do not have semvers. contracts := []contractToCheck{ - {predeploys.L2ToL1MessagePasserAddr, "L2ToL1MessagePasser"}, - {predeploys.DeployerWhitelistAddr, "DeployerWhitelist"}, - {predeploys.WETHAddr, "WETH"}, - {predeploys.L2CrossDomainMessengerAddr, "L2CrossDomainMessenger"}, - {predeploys.L2StandardBridgeAddr, "L2StandardBridge"}, - {predeploys.SequencerFeeVaultAddr, "SequencerFeeVault"}, - {predeploys.OptimismMintableERC20FactoryAddr, "OptimismMintableERC20Factory"}, - {predeploys.L1BlockNumberAddr, "L1BlockNumber"}, - {predeploys.GasPriceOracleAddr, "GasPriceOracle"}, - {predeploys.L1BlockAddr, "L1Block"}, - {predeploys.LegacyMessagePasserAddr, "LegacyMessagePasser"}, - {predeploys.L2ERC721BridgeAddr, "L2ERC721Bridge"}, - {predeploys.OptimismMintableERC721FactoryAddr, "OptimismMintableERC721Factory"}, - {predeploys.BaseFeeVaultAddr, "BaseFeeVault"}, - {predeploys.L1FeeVaultAddr, "L1FeeVault"}, - {predeploys.SchemaRegistryAddr, "SchemaRegistry"}, - {predeploys.EASAddr, "EAS"}, - {predeploys.WETHAddr, "WETH"}, + {predeploys.L2ToL1MessagePasserAddr, &ps.L2ToL1MessagePasser, "L2ToL1MessagePasser"}, + {predeploys.DeployerWhitelistAddr, &ps.DeployerWhitelist, "DeployerWhitelist"}, + {predeploys.WETHAddr, &ps.WETH, "WETH"}, + {predeploys.L2CrossDomainMessengerAddr, &ps.L2CrossDomainMessenger, "L2CrossDomainMessenger"}, + {predeploys.L2StandardBridgeAddr, &ps.L2StandardBridge, "L2StandardBridge"}, + {predeploys.SequencerFeeVaultAddr, &ps.SequencerFeeVault, "SequencerFeeVault"}, + {predeploys.OptimismMintableERC20FactoryAddr, &ps.OptimismMintableERC20Factory, "OptimismMintableERC20Factory"}, + {predeploys.L1BlockNumberAddr, &ps.L1BlockNumber, "L1BlockNumber"}, + {predeploys.GasPriceOracleAddr, &ps.GasPriceOracle, "GasPriceOracle"}, + {predeploys.L1BlockAddr, &ps.L1Block, "L1Block"}, + {predeploys.LegacyMessagePasserAddr, &ps.LegacyMessagePasser, "LegacyMessagePasser"}, + {predeploys.L2ERC721BridgeAddr, &ps.L2ERC721Bridge, "L2ERC721Bridge"}, + {predeploys.OptimismMintableERC721FactoryAddr, &ps.OptimismMintableERC721Factory, "OptimismMintableERC721Factory"}, + {predeploys.BaseFeeVaultAddr, &ps.BaseFeeVault, "BaseFeeVault"}, + {predeploys.L1FeeVaultAddr, &ps.L1FeeVault, "L1FeeVault"}, + {predeploys.SchemaRegistryAddr, &ps.SchemaRegistry, "SchemaRegistry"}, + {predeploys.EASAddr, &ps.EAS, "EAS"}, } for _, contract := range contracts { - data, _, err := host.Call( - addr, - contract.Address, - bytes.Clone(versionSelector), - 1_000_000_000, - uint256.NewInt(0), - ) + semver, err := ReadSemver(host, contract.Address) if err != nil { - return fmt.Errorf("failed to call version on %s: %w", contract.Name, err) - } - - // The second 32 bytes contain the length of the string - length := new(big.Int).SetBytes(data[32:64]).Int64() - // Start of the string data (after offset and length) - stringStart := 64 - stringEnd := int64(stringStart) + length - - // Bounds check - if stringEnd > int64(len(data)) { - return fmt.Errorf("string data out of bounds") + return nil, fmt.Errorf("failed to read semver for %s: %w", contract.Name, err) } - contractsOutput[contract.Name] = string(data[stringStart:stringEnd]) + *contract.FieldPtr = semver } erc20Semver, err := findSemverBytecode(host, predeploys.OptimismMintableERC20FactoryAddr) if err == nil { - contractsOutput["OptimismMintableERC20"] = erc20Semver + ps.OptimismMintableERC20 = erc20Semver } else { l.Warn("failed to find semver for OptimismMintableERC20", "err", err) } erc721Semver, err := findSemverBytecode(host, predeploys.OptimismMintableERC721FactoryAddr) if err == nil { - contractsOutput["OptimismMintableERC721"] = erc721Semver + ps.OptimismMintableERC721 = erc721Semver } else { l.Warn("failed to find semver for OptimismMintableERC721", "err", err) } - if err := jsonutil.WriteJSON(contractsOutput, ioutil.ToStdOutOrFileOrNoop(cliCfg.Outfile, 0o666)); err != nil { - return fmt.Errorf("failed to write rollup config: %w", err) + return &ps, nil +} + +var versionSelector = []byte{0x54, 0xfd, 0x4d, 0x50} + +func ReadSemver(host *script.Host, addr common.Address) (string, error) { + data, _, err := host.Call( + common.Address{19: 0x01}, + addr, + bytes.Clone(versionSelector), + 1_000_000_000, + uint256.NewInt(0), + ) + if err != nil { + return "", fmt.Errorf("failed to call version on %s: %w", addr, err) } - return nil + // The second 32 bytes contain the length of the string + length := new(big.Int).SetBytes(data[32:64]).Int64() + // Start of the string data (after offset and length) + stringStart := 64 + stringEnd := int64(stringStart) + length + + // Bounds check + if stringEnd > int64(len(data)) { + return "", fmt.Errorf("string data out of bounds") + } + + return string(data[stringStart:stringEnd]), nil } const patternLen = 24 diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index bcbc14eb7f19..7f4c0cb06deb 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -1,12 +1,16 @@ package integration_test import ( + "bufio" "bytes" + "compress/gzip" "context" "crypto/rand" "encoding/hex" + "encoding/json" "fmt" "log/slog" + "maps" "math/big" "os" "testing" @@ -182,13 +186,15 @@ func TestApplyExistingOPCM(t *testing.T) { l2ChainID := uint256.NewInt(1) + // Hardcode the below tags to ensure the test is validating the correct + // version even if the underlying tag changes intent, st := newIntent( t, l1ChainID, dk, l2ChainID, - artifacts.DefaultL1ContractsLocator, - artifacts.DefaultL2ContractsLocator, + artifacts.MustNewLocatorFromTag("op-contracts/v1.6.0"), + artifacts.MustNewLocatorFromTag("op-contracts/v1.7.0-beta.1+l2-contracts"), ) // Define a new create2 salt to avoid contract address collisions _, err = rand.Read(st.Create2Salt[:]) @@ -231,6 +237,130 @@ func TestApplyExistingOPCM(t *testing.T) { require.Equal(t, tt.expAddr, tt.actAddr) }) } + + artifactsFSL2, cleanupL2, err := artifacts.Download( + ctx, + intent.L2ContractsLocator, + artifacts.LogProgressor(lgr), + ) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, cleanupL2()) + }) + + chainState := st.Chains[0] + chainIntent := intent.Chains[0] + + semvers, err := inspect.L2Semvers(inspect.L2SemversConfig{ + Lgr: lgr, + Artifacts: artifactsFSL2, + ChainState: chainState, + }) + require.NoError(t, err) + + expectedSemversL2 := &inspect.L2PredeploySemvers{ + L2ToL1MessagePasser: "1.1.1-beta.1", + DeployerWhitelist: "1.1.1-beta.1", + WETH: "1.0.0-beta.1", + L2CrossDomainMessenger: "2.1.1-beta.1", + L2StandardBridge: "1.11.1-beta.1", + SequencerFeeVault: "1.5.0-beta.2", + OptimismMintableERC20Factory: "1.10.1-beta.2", + L1BlockNumber: "1.1.1-beta.1", + GasPriceOracle: "1.3.1-beta.1", + L1Block: "1.5.1-beta.1", + LegacyMessagePasser: "1.1.1-beta.1", + L2ERC721Bridge: "1.7.1-beta.2", + OptimismMintableERC721Factory: "1.4.1-beta.1", + BaseFeeVault: "1.5.0-beta.2", + L1FeeVault: "1.5.0-beta.2", + SchemaRegistry: "1.3.1-beta.1", + EAS: "1.4.1-beta.1", + CrossL2Inbox: "", + L2toL2CrossDomainMessenger: "", + SuperchainWETH: "", + ETHLiquidity: "", + SuperchainTokenBridge: "", + OptimismMintableERC20: "1.4.0-beta.1", + OptimismMintableERC721: "1.3.1-beta.1", + } + + require.EqualValues(t, expectedSemversL2, semvers) + + f, err := os.Open("./testdata/allocs-l2-v160.json.gz") + require.NoError(t, err) + defer f.Close() + gzr, err := gzip.NewReader(f) + require.NoError(t, err) + defer gzr.Close() + dec := json.NewDecoder(bufio.NewReader(gzr)) + var expAllocs types.GenesisAlloc + require.NoError(t, dec.Decode(&expAllocs)) + + type storageCheckerFunc func(addr common.Address, actStorage map[common.Hash]common.Hash) + + storageDiff := func(addr common.Address, expStorage, actStorage map[common.Hash]common.Hash) { + require.EqualValues(t, expStorage, actStorage, "storage for %s differs", addr) + } + + defaultStorageChecker := func(addr common.Address, actStorage map[common.Hash]common.Hash) { + storageDiff(addr, expAllocs[addr].Storage, actStorage) + } + + overrideStorageChecker := func(addr common.Address, actStorage, overrides map[common.Hash]common.Hash) { + expStorage := make(map[common.Hash]common.Hash) + maps.Copy(expStorage, expAllocs[addr].Storage) + maps.Copy(expStorage, overrides) + storageDiff(addr, expStorage, actStorage) + } + + storageCheckers := map[common.Address]storageCheckerFunc{ + predeploys.L2CrossDomainMessengerAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) { + overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{ + {31: 0xcf}: common.BytesToHash(chainState.L1CrossDomainMessengerProxyAddress.Bytes()), + }) + }, + predeploys.L2StandardBridgeAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) { + overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{ + {31: 0x04}: common.BytesToHash(chainState.L1StandardBridgeProxyAddress.Bytes()), + }) + }, + predeploys.L2ERC721BridgeAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) { + overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{ + {31: 0x02}: common.BytesToHash(chainState.L1ERC721BridgeProxyAddress.Bytes()), + }) + }, + predeploys.ProxyAdminAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) { + overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{ + {}: common.BytesToHash(intent.Chains[0].Roles.L2ProxyAdminOwner.Bytes()), + }) + }, + // The ProxyAdmin owner is also set on the ProxyAdmin contract's implementation address, see + // L2Genesis.s.sol line 292. + common.HexToAddress("0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30018"): func(addr common.Address, actStorage map[common.Hash]common.Hash) { + overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{ + {}: common.BytesToHash(chainIntent.Roles.L2ProxyAdminOwner.Bytes()), + }) + }, + } + + //Use a custom equality function to compare the genesis allocs + //because the reflect-based one is really slow + actAllocs := st.Chains[0].Allocs.Data.Accounts + require.Equal(t, len(expAllocs), len(actAllocs)) + for addr, expAcc := range expAllocs { + actAcc, ok := actAllocs[addr] + require.True(t, ok) + require.True(t, expAcc.Balance.Cmp(actAcc.Balance) == 0, "balance for %s differs", addr) + require.Equal(t, expAcc.Nonce, actAcc.Nonce, "nonce for %s differs", addr) + require.Equal(t, hex.EncodeToString(expAllocs[addr].Code), hex.EncodeToString(actAcc.Code), "code for %s differs", addr) + + storageChecker, ok := storageCheckers[addr] + if !ok { + storageChecker = defaultStorageChecker + } + storageChecker(addr, actAcc.Storage) + } } func TestL2BlockTimeOverride(t *testing.T) { diff --git a/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160.json.gz b/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..545dc3397bdd3220146c7c6c150ec44fcd19b457 GIT binary patch literal 152466 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

hh!E55=d%TkW0x6GS8%HYjFwYh7A@bc8$SE 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 literal 0 HcmV?d00001 From 7359d15c3a16a91f47a82ff6566721c68d40ff58 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Tue, 19 Nov 2024 03:48:49 +0100 Subject: [PATCH 200/451] improve tests L2CrossDomainMessenger_Test (#12934) * improve tests * fixes * fixes... * Apply suggestions from code review Co-authored-by: smartcontracts * fixes --------- Co-authored-by: smartcontracts --- .../test/L2/L2CrossDomainMessenger.t.sol | 97 ++++++++++++++++++- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol index 131851783c79..f5ef2d635595 100644 --- a/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2CrossDomainMessenger.t.sol @@ -5,6 +5,7 @@ pragma solidity 0.8.15; import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; +import { stdError } from "forge-std/StdError.sol"; // Libraries import { Hashing } from "src/libraries/Hashing.sol"; @@ -148,17 +149,103 @@ contract L2CrossDomainMessenger_Test is CommonTest { assertEq(l2CrossDomainMessenger.failedMessages(hash), false); } - /// @dev Tests that `relayMessage` reverts if attempting to relay - /// a message sent to an L1 system contract. - function test_relayMessage_toSystemContract_reverts() external { - address target = address(l2ToL1MessagePasser); + /// @dev Tests that relayMessage reverts if the value sent does not match the amount + function test_relayMessage_fromOtherMessengerValueMismatch_reverts() external { + // set the target to be alice + address target = alice; address sender = address(l1CrossDomainMessenger); address caller = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); bytes memory message = hex"1111"; + // cannot send a message where the amount inputted does not match the msg.value + vm.deal(caller, 10 ether); vm.prank(caller); + vm.expectRevert(stdError.assertionError); + l2CrossDomainMessenger.relayMessage{ value: 10 ether }( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 9 ether, 0, message + ); + } + + /// @dev Tests that relayMessage reverts if a failed message is attempted to be replayed and the caller is the other + /// messenger + function test_relayMessage_fromOtherMessengerFailedMessageReplay_reverts() external { + // set the target to be alice + address target = alice; + address sender = address(l1CrossDomainMessenger); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); + bytes memory message = hex"1111"; + + // make a failed message + vm.etch(target, hex"fe"); + vm.prank(caller); + l2CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message + ); + + // cannot replay messages when the caller is the other messenger + vm.prank(caller); + vm.expectRevert(stdError.assertionError); + l2CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message + ); + } + + /// @dev Tests that relayMessage reverts if attempting to relay a message + /// sent to self + function test_relayMessage_toSelf_reverts() external { + address sender = address(l1CrossDomainMessenger); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(0), bytes32(abi.encode(sender))); + + vm.prank(caller); + vm.expectRevert("CrossDomainMessenger: cannot send message to blocked system address"); + l2CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), + sender, + address(l2CrossDomainMessenger), + 0, + 0, + message + ); + } + + /// @dev Tests that relayMessage reverts if attempting to relay a message + /// sent to the l2ToL1MessagePasser address + function test_relayMessage_toL2ToL1MessagePasser_reverts() external { + address sender = address(l1CrossDomainMessenger); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger)); + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(0), bytes32(abi.encode(sender))); + + vm.prank(caller); + vm.expectRevert("CrossDomainMessenger: cannot send message to blocked system address"); + l2CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), + sender, + address(l2ToL1MessagePasser), + 0, + 0, + message + ); + } + + /// @dev Tests that the relayMessage function reverts if the message called by non-optimismPortal but not a failed + /// message + function test_relayMessage_relayingNewMessageByExternalUser_reverts() external { + address target = address(alice); + address sender = address(l1CrossDomainMessenger); + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(0), bytes32(abi.encode(sender))); + + vm.prank(bob); vm.expectRevert("CrossDomainMessenger: message cannot be replayed"); - l1CrossDomainMessenger.relayMessage(Encoding.encodeVersionedNonce(0, 1), sender, target, 0, 0, message); + l2CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message + ); } /// @dev Tests that `relayMessage` correctly resets the `xDomainMessageSender` From e32278e897db31045def5cc9fd70981f8e95adb7 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Tue, 19 Nov 2024 03:49:43 +0100 Subject: [PATCH 201/451] improve tests of L1CrossDomainMessenger_Test (#12933) * improve tests * fixes * fixes * fixes * Apply suggestions from code review Co-authored-by: smartcontracts * fixes --------- Co-authored-by: smartcontracts --- .../test/L1/L1CrossDomainMessenger.t.sol | 87 +++++++++++++++++-- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 23c1365e9156..22345d860e42 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.15; // Testing utilities import { CommonTest } from "test/setup/CommonTest.sol"; import { Reverter } from "test/mocks/Callers.sol"; +import { stdError } from "forge-std/StdError.sol"; // Libraries import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; @@ -173,21 +174,93 @@ contract L1CrossDomainMessenger_Test is CommonTest { assertEq(l1CrossDomainMessenger.failedMessages(hash), false); } - /// @dev Tests that relayMessage reverts if attempting to relay a message - /// sent to an L1 system contract. - function test_relayMessage_toSystemContract_reverts() external { - // set the target to be the OptimismPortal - address target = address(optimismPortal); + /// @dev Tests that relayMessage reverts if caller is optimismPortal and the value sent does not match the amount + function test_relayMessage_fromOtherMessengerValueMismatch_reverts() external { + address target = alice; address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER; bytes memory message = hex"1111"; + // set the value of op.l2Sender() to be the L2CrossDomainMessenger. + vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); + + // correctly sending as OptimismPortal but amount does not match msg.value + vm.deal(address(optimismPortal), 10 ether); + vm.prank(address(optimismPortal)); + vm.expectRevert(stdError.assertionError); + l1CrossDomainMessenger.relayMessage{ value: 10 ether }( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 9 ether, 0, message + ); + } + + /// @dev Tests that relayMessage reverts if a failed message is attempted to be replayed via the optimismPortal + function test_relayMessage_fromOtherMessengerFailedMessageReplay_reverts() external { + address target = alice; + address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER; + bytes memory message = hex"1111"; + + // set the value of op.l2Sender() to be the L2 Cross Domain Messenger. + vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); + + // make a failed message + vm.etch(target, hex"fe"); vm.prank(address(optimismPortal)); - vm.expectRevert("CrossDomainMessenger: message cannot be replayed"); l1CrossDomainMessenger.relayMessage( Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message ); - vm.store(address(optimismPortal), 0, bytes32(abi.encode(sender))); + // cannot replay messages when optimism portal is msg.sender + vm.prank(address(optimismPortal)); + vm.expectRevert(stdError.assertionError); + l1CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message + ); + } + + /// @dev Tests that relayMessage reverts if attempting to relay a message + /// with l1CrossDomainMessenger as the target + function test_relayMessage_toSelf_reverts() external { + address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER; + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); + + vm.prank(address(optimismPortal)); + vm.expectRevert("CrossDomainMessenger: cannot send message to blocked system address"); + l1CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), + sender, + address(l1CrossDomainMessenger), + 0, + 0, + message + ); + } + + /// @dev Tests that relayMessage reverts if attempting to relay a message + /// with optimismPortal as the target + function test_relayMessage_toOptimismPortal_reverts() external { + address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER; + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); + + vm.prank(address(optimismPortal)); + vm.expectRevert("CrossDomainMessenger: cannot send message to blocked system address"); + l1CrossDomainMessenger.relayMessage( + Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, address(optimismPortal), 0, 0, message + ); + } + + /// @dev Tests that the relayMessage function reverts if the message called by non-optimismPortal but not a failed + /// message + function test_relayMessage_relayingNewMessageByExternalUser_reverts() external { + address target = address(alice); + address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER; + bytes memory message = hex"1111"; + + vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); + + vm.prank(bob); vm.expectRevert("CrossDomainMessenger: message cannot be replayed"); l1CrossDomainMessenger.relayMessage( Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }), sender, target, 0, 0, message From 357fc7d44d38f7b1c0f8bfbe5b689dc6205257a9 Mon Sep 17 00:00:00 2001 From: Michael Amadi Date: Tue, 19 Nov 2024 03:50:36 +0100 Subject: [PATCH 202/451] fix challenge struct natspec (#12951) * fix challenge struct natspec * semver bump * semver bump * semver bump --- packages/contracts-bedrock/snapshots/semver-lock.json | 4 ++-- .../contracts-bedrock/src/L1/DataAvailabilityChallenge.sol | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index dd2618cc5a7e..5ba3a4b6a165 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -1,7 +1,7 @@ { "src/L1/DataAvailabilityChallenge.sol": { - "initCodeHash": "0xbd00d6568abab3e7fc211c40d682862242f25493010a4a097bd1f3b45c8c87c3", - "sourceCodeHash": "0x58b587034a67b4bb718abbaded8ac23b082c0971105874bcc42c23f051c67f6e" + "initCodeHash": "0x240a9b695e1ab73692672b907c2ae147ee9224e95a03c02d99333afe186c3d2f", + "sourceCodeHash": "0xc6ab0e64cfbdcfa6de0a480426263712b3ddbabe56a88ec7c02556cb432e6b02" }, "src/L1/L1CrossDomainMessenger.sol": { "initCodeHash": "0x2e9cb3ceb5e55341b311f0666ef7655df4fafae75afdfbcd701cd9c9b2b017d5", diff --git a/packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol b/packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol index 2a725fc4f200..666e4306b482 100644 --- a/packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol +++ b/packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol @@ -24,9 +24,10 @@ enum CommitmentType { } /// @dev A struct representing a single DA challenge. -/// @custom:field status The status of the challenge. /// @custom:field challenger The address that initiated the challenge. +/// @custom:field lockedBond The amount of ETH bond that was locked by the challenger. /// @custom:field startBlock The block number at which the challenge was initiated. +/// @custom:field resolvedBlock The block number at which the challenge was resolved. struct Challenge { address challenger; uint256 lockedBond; @@ -94,8 +95,8 @@ contract DataAvailabilityChallenge is OwnableUpgradeable, ISemver { event BalanceChanged(address account, uint256 balance); /// @notice Semantic version. - /// @custom:semver 1.0.1-beta.2 - string public constant version = "1.0.1-beta.2"; + /// @custom:semver 1.0.1-beta.3 + string public constant version = "1.0.1-beta.3"; /// @notice The fixed cost of resolving a challenge. /// @dev The value is estimated by measuring the cost of resolving with `bytes(0)` From 99150d3df6d36c1f2c518645916ad2804effe717 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 19 Nov 2024 14:02:53 +1000 Subject: [PATCH 203/451] security-reviews: Add 3Doc report on Canon F_GETFD changes. (#12960) * security-reviews: Add 3Doc report on Canon F_GETFD changes. * security-reviews: Update README. --- .../2024_10-Cannon-FGETFD-3DocSecurity.md | 114 ++++++++++++++++++ docs/security-reviews/README.md | 3 +- 2 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 docs/security-reviews/2024_10-Cannon-FGETFD-3DocSecurity.md diff --git a/docs/security-reviews/2024_10-Cannon-FGETFD-3DocSecurity.md b/docs/security-reviews/2024_10-Cannon-FGETFD-3DocSecurity.md new file mode 100644 index 000000000000..f157356e4905 --- /dev/null +++ b/docs/security-reviews/2024_10-Cannon-FGETFD-3DocSecurity.md @@ -0,0 +1,114 @@ +# Audit Report - OP Cannon + +| | | +| -------------- | ------------------------------------------------------------------------- | +| **Audit Date** | Oct 2nd 2024 - Oct 3rd 2024 | +| **Auditor** | 3DOC Security ([@3docSec](https://x.com/3docSec)) | +| **Version 1** | Oct 3rd 2024. | + +
+ +# Contents +- [Audit Report - OP cannon](#audit-report---op-cannon) +- [Contents](#contents) +- [Disclaimer](#disclaimer) +- [About 3DOC](#about-3doc) +- [Scope](#scope) +- [Severity Classification](#severity-classification) +- [Summary](#summary) +- [Findings](#findings) + - [Low Risk Findings (1)](#low-risk-findings-1) + - [1. Op-challenger Docker image does not include Cannon embeds](#-op-challenger-docker-image-does-not-include-cannon-embeds) + +# Disclaimer +_The following audit report is based on the information and code provided by the client, and any findings or recommendations are made solely on the basis of this information. While the Auditor has exercised due care and skill in conducting the audit, it cannot be guaranteed that all issues have been identified and that there are no undiscovered errors or vulnerabilities in the code._ + +_Furthermore, this report is not an endorsement or certification of the protocol, and the Auditor does not assume any responsibility for any losses or damages that may result from the use of the smart contracts, either in their current form or in any modified version thereof._ + +# About 3DOC +3DOC is a top ranked Smart Contract Auditor doing audits on Code4rena (www.code4rena.com), having ranked 1st in multiple contests in [solo](https://code4rena.com/@3docSec) and [team](https://code4rena.com/@RadiantLabs) audits, including the [Optimism superchain contest](https://code4rena.com/audits/2024-07-optimism-superchain) in July 2024.
+He can also be booked for conducting Private Audits. + +Contact:
+ +X: [@3DocSec](https://x.com/3DocSec) + +e-mail: [hello@3doc.fr](mailto:hello@3doc.fr) + +# Scope +The scope of the audit is the following Pull Request in the client's GitHub repository: + +https://github.com/ethereum-optimism/optimism/pull/12050 + +The change consists of a core update for supporting the `F_GETFD` syscall in the MIPS VM, [provided with this commit](https://github.com/ethereum-optimism/optimism/pull/12050/commits/7c8257d3574a2a76ab90f8129c7b532d68049944), and several additional updates accommodating the VM version bump that came with the core change. + +# Severity Classification +| Severity | Impact: High | Impact: Medium | Impact: Low | +| ---------------------- | ------------ | -------------- | ----------- | +| **Likelihood: High** | ![high] | ![high] | ![medium] | +| **Likelihood: Medium** | ![high] | ![medium] | ![low] | +| **Likelihood: Low** | ![medium] | ![low] | ![low] | + +**Impact** - the technical, economic and reputation damage of a successful attack + +**Likelihood** - the chance that a particular vulnerability is discovered and exploited + +# Summary + +| Severity | Total | +| -------------- | ----- | +| ![high] | 0 | +| ![medium] | 0 | +| ![low] | 0 | +| ![information] | 0 | + + +# Findings +## Low Risk findings (0) + +### [False positive] Op-challenger Docker image does not include Cannon embeds +#### Description +The change in scope added a new implementation of the Cannon VM, which was called `VersionSingleThreaded2`. Cannon has now three versions (`VersionSingleThreaded`, `VersionSingleThreaded2`, and `VersionMultiThreaded`). + +The op-challenger program makes use of the Cannon VM in several places via the configured `VmBin` path, which point to the `multicannon` command line. This one reads the State version from the input state and selects the right Cannon VM accordingly (`cannon/multicannon/exec.go:L81`). + +If we look at the Docker challenger image generated by the `make golang-docker` command, however, we can see it doesn't contain an `embeds` folder: + +``` +docker run -t us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger find / -name embeds +``` + +But it however has the `cannon` command pointing to the `multicannon` multiplexer: + +``` +➜ optimism git:(52d0e60c1) ✗ docker run -t us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger cannon | head -2 +NAME: + multicannon - MIPS Fault Proof tool +➜ optimism git:(52d0e60c1) ✗ +``` + +This issue appears to be pre-existing to the changes in scope; using Docker images to run the challenger is [mentioned as option](https://docs.optimism.io/builders/chain-operators/tools/op-challenger), but only as alternative option, hence the Low risk assessed for this finding. + +#### Impact +Because of this issue, challenger instances operated in a Docker container won't be able to function properly. + +#### Recommendation +Consider modifying the Docker build chain to include the `embeds` folder. +Consider extending the current e2e test suite to cover execution from Docker images. + +#### Discussion + +> @inphi The cannon-2 implementation that supports go1.22 is now embedded into the cannon cli binary. Note that these embeds are not actual files that you can find in the docker container filesystem. But rather an embedded filesystem inside the Go binary - https://pkg.go.dev/embed. + +> @3DOC Oh yes I see that. So those are included in an embedded filesystem, I missed that + + +[high]: https://img.shields.io/badge/-HIGH-b02319 "HIGH" +[medium]: https://img.shields.io/badge/-MEDIUM-orange "MEDIUM" +[low]: https://img.shields.io/badge/-LOW-FFD700 "LOW" +[information]: https://img.shields.io/badge/-INFORMATION-darkgreen "INFORMATION" +[fixed]: https://img.shields.io/badge/-FIXED-brightgreen "FIXED" +[acknowledged]: https://img.shields.io/badge/-ACKNOWLEDGED-blue "ACKNOWLEDGED" +[disputed]: https://img.shields.io/badge/-DISPUTED-lightgrey "DISPUTED" +[reported]: https://img.shields.io/badge/-REPORTED-lightblue "REPORTED" +[partiallyfixed]: https://img.shields.io/badge/-PARTIALLY_FIXED-lightgreen "PARTIALLTY FIXED" diff --git a/docs/security-reviews/README.md b/docs/security-reviews/README.md index 483dc1541858..5639b74410b8 100644 --- a/docs/security-reviews/README.md +++ b/docs/security-reviews/README.md @@ -6,7 +6,7 @@ Each review is focused on a different part of the codebase, and at a different p Please see the report for the specific details. | Date | Reviewer | Focus and Scope | Report Link | Commit | Subsequent Release | -| ------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------- | +|---------|----------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------| -------------------------------------------- |---------------------| | 2020-10 | Trail of Bits | Rollup | [2020_10-TrailOfBits.pdf](./2020_10-Rollup-TrailOfBits.pdf) | | | | 2020-11 | Dapphub | ECDSA Wallet | [2020_11-Dapphub-ECDSA_Wallet.pdf](./2020_11-Dapphub-ECDSA_Wallet.pdf) | | | | 2021-03 | OpenZeppelin | OVM and Rollup | [2021_03-OVM_and_Rollup-OpenZeppelin.pdf](./2021_03-OVM_and_Rollup-OpenZeppelin.pdf) | | | @@ -27,5 +27,6 @@ Please see the report for the specific details. | 2024-03 | Sherlock | Fault Proofs | Sherlock Optimism Fault Proofs Contest ([site](https://audits.sherlock.xyz/contests/205), [repo](https://github.com/sherlock-audit/2024-02-optimism-2024)) | | | | 2024-08 | Cantina | Fault proof no-MIPS: All contracts in the `packages/contracts-bedrock/src/dispute` directory | [./2024_08_Fault-Proofs-MIPS_Cantina.pdf](./2024_08_Fault-Proofs-MIPS_Cantina.pdf) | 1f7081798ce2d49b8643514663d10681cb853a3d | op-contracts/v1.4.0 | | 2024-08 | Spearbit | Fault proof MIPS: `MIPS.sol` | [./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf](./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf) | 71b93116738ee98c9f8713b1a5dfe626ce06c1b2 | op-contracts/v1.6.0 | +| 2024-10 | 3Doc Security | Fault proof MIPS: `MIPS.sol` | [./2024_10-Cannon-FGETFD-3DocSecurity.md](./2024_10-Cannon-FGETFD-3DocSecurity.md) | 52d0e60c16498ad4efec8798e3fc1b36b13f46a2 | op-contracts/v1.8.0 | [kontrol]: https://github.com/ethereum-optimism/optimism/blob/876e16ad04968f0bb641eb76f98eb77e7e1a3e16/packages/contracts-bedrock/test/kontrol/README.md From c4a44c977d55df770d2ad4b9f0dd521beb64f11c Mon Sep 17 00:00:00 2001 From: BE Water Date: Tue, 19 Nov 2024 08:58:31 +0400 Subject: [PATCH 204/451] make the solidity code path correct. (#12959) --- packages/contracts-bedrock/meta/STYLE_GUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/meta/STYLE_GUIDE.md b/packages/contracts-bedrock/meta/STYLE_GUIDE.md index 1d8b84818b9b..00af13d5a5b4 100644 --- a/packages/contracts-bedrock/meta/STYLE_GUIDE.md +++ b/packages/contracts-bedrock/meta/STYLE_GUIDE.md @@ -96,8 +96,8 @@ Spacers MUST be `private`. All contracts should be assumed to live behind proxies (except in certain special circumstances). This means that new contracts MUST be built under the assumption of upgradeability. -We use a minimal [`Proxy`](./src/universal/Proxy.sol) contract designed to be owned by a -corresponding [`ProxyAdmin`](./src/universal/ProxyAdmin.sol) which follow the interfaces +We use a minimal [`Proxy`](../src/universal/Proxy.sol) contract designed to be owned by a +corresponding [`ProxyAdmin`](../src/universal/ProxyAdmin.sol) which follow the interfaces of OpenZeppelin's `Proxy` and `ProxyAdmin` contracts, respectively. Unless explicitly discussed otherwise, you MUST include the following basic upgradeability From 52e9be8aed15b17249bf47c10c22728e6c71fc62 Mon Sep 17 00:00:00 2001 From: brawn Date: Tue, 19 Nov 2024 08:08:11 +0300 Subject: [PATCH 205/451] Typo fix Update build-legacy-cannons.sh (#12955) Fix typo in build script comment for clarity --- cannon/scripts/build-legacy-cannons.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cannon/scripts/build-legacy-cannons.sh b/cannon/scripts/build-legacy-cannons.sh index 62b543839841..3df0a43c31ea 100755 --- a/cannon/scripts/build-legacy-cannons.sh +++ b/cannon/scripts/build-legacy-cannons.sh @@ -3,7 +3,7 @@ set -euo pipefail SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # This script builds a version of the cannon executable that includes support for both current and legacy state versions. -# Each cannon release is built +# Each cannon release is built separately. TMP_DIR=$(mktemp -d) function cleanup() { From d67530964d6da9b87db48cfffc48fb7e212219ee Mon Sep 17 00:00:00 2001 From: Donny <130464015+defitricks@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:11:24 +0200 Subject: [PATCH 206/451] Typo Update pr-guidelines.md (#12929) CI must passing should be CI must pass. --- docs/handbook/pr-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/pr-guidelines.md b/docs/handbook/pr-guidelines.md index cf133ada6c57..57913ebeaa51 100644 --- a/docs/handbook/pr-guidelines.md +++ b/docs/handbook/pr-guidelines.md @@ -46,4 +46,4 @@ This is organized by current state of PR, so it can be easily referenced frequen ### Merging PRs - **Resolve all Comments**: Comments can be resolved by (1) the PR author for nits/optionals, (2) the author or reviewer after discussions, or (3) extracting the comment into an issue to address in a future PR. For (3), ensure the new issue links to the specific comment thread. This is currently enforced by GitHub's merge requirements. -- **Other Standard Merge Requirements**: The PR must be approved by the appropriate reviewers, CI must passing, and other standard merge requirements apply. +- **Other Standard Merge Requirements**: The PR must be approved by the appropriate reviewers, CI must pass, and other standard merge requirements apply. From 3d50ab9eda5e087137257ad23ee6ac727b105771 Mon Sep 17 00:00:00 2001 From: soyboy <85043086+sbvegan@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:14:52 +0800 Subject: [PATCH 207/451] Update README.md (#12945) - fixing the audit commits and descriptions so they map correctly --- docs/security-reviews/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/security-reviews/README.md b/docs/security-reviews/README.md index 5639b74410b8..265d0a65f903 100644 --- a/docs/security-reviews/README.md +++ b/docs/security-reviews/README.md @@ -25,8 +25,8 @@ Please see the report for the specific details. | 2024-02 | Runtime Verification | Pausability | [Kontrol Verification][kontrol] | | | | 2024-02 | Cantina | MCP L1: `OptimismPortal.sol`, `L1CrossDomainMessenger.sol`, `L1StandardBridge.sol`, `L1ERC721Bridge.sol`, `OptimismMintableERC20Factory.sol`, `L2OutputOracle.sol`, `SystemConfig.sol` | [2024_02-MCP_L1-Cantina.pdf](./2024_02-MCP_L1-Cantina.pdf) | e6ef3a900c42c8722e72c2e2314027f85d12ced5 | op-contracts/v1.3.0 | | 2024-03 | Sherlock | Fault Proofs | Sherlock Optimism Fault Proofs Contest ([site](https://audits.sherlock.xyz/contests/205), [repo](https://github.com/sherlock-audit/2024-02-optimism-2024)) | | | -| 2024-08 | Cantina | Fault proof no-MIPS: All contracts in the `packages/contracts-bedrock/src/dispute` directory | [./2024_08_Fault-Proofs-MIPS_Cantina.pdf](./2024_08_Fault-Proofs-MIPS_Cantina.pdf) | 1f7081798ce2d49b8643514663d10681cb853a3d | op-contracts/v1.4.0 | -| 2024-08 | Spearbit | Fault proof MIPS: `MIPS.sol` | [./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf](./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf) | 71b93116738ee98c9f8713b1a5dfe626ce06c1b2 | op-contracts/v1.6.0 | +| 2024-08 | Cantina | Fault proof MIPS: `MIPS.sol` | [./2024_08_Fault-Proofs-MIPS_Cantina.pdf](./2024_08_Fault-Proofs-MIPS_Cantina.pdf) | 71b93116738ee98c9f8713b1a5dfe626ce06c1b2 | op-contracts/v1.4.0 | +| 2024-08 | Spearbit | Fault proof no-MIPS: All contracts in the `packages/contracts-bedrock/src/dispute` directory | [./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf](./2024_08_Fault-Proofs-No-MIPS_Spearbit.pdf) | 1f7081798ce2d49b8643514663d10681cb853a3d | op-contracts/v1.6.0 | | 2024-10 | 3Doc Security | Fault proof MIPS: `MIPS.sol` | [./2024_10-Cannon-FGETFD-3DocSecurity.md](./2024_10-Cannon-FGETFD-3DocSecurity.md) | 52d0e60c16498ad4efec8798e3fc1b36b13f46a2 | op-contracts/v1.8.0 | [kontrol]: https://github.com/ethereum-optimism/optimism/blob/876e16ad04968f0bb641eb76f98eb77e7e1a3e16/packages/contracts-bedrock/test/kontrol/README.md From fbcfb837de427c43a57e76de2373d9c76fce873e Mon Sep 17 00:00:00 2001 From: brawn Date: Tue, 19 Nov 2024 10:27:18 +0300 Subject: [PATCH 208/451] Typo fix Update commitment.go (#12961) Fix typo in comment for Keccak256Commitment.Encode method --- op-alt-da/commitment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-alt-da/commitment.go b/op-alt-da/commitment.go index cc5829ad4dc6..a6fa5424665b 100644 --- a/op-alt-da/commitment.go +++ b/op-alt-da/commitment.go @@ -108,7 +108,7 @@ func (c Keccak256Commitment) CommitmentType() CommitmentType { return Keccak256CommitmentType } -// Encode adds a commitment type prefix self describing the commitment. +// Encode adds a commitment type prefix that describes the commitment. func (c Keccak256Commitment) Encode() []byte { return append([]byte{byte(Keccak256CommitmentType)}, c...) } From 3e7c1dd69d48c1edefd593abe555fd2fd8eec9be Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Tue, 19 Nov 2024 15:28:01 +0800 Subject: [PATCH 209/451] fix typos (#12910) --- .../contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index 74492556e1b1..5e35e8848c8a 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -60,9 +60,9 @@ import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; // we use variable names that are shorthand for the full contract names, for example: // - `dsi` for DeploySuperchainInput // - `dso` for DeploySuperchainOutput -// - `dio` for DeployImplementationsInput +// - `dii` for DeployImplementationsInput // - `dio` for DeployImplementationsOutput -// - `doo` for DeployOPChainInput +// - `doi` for DeployOPChainInput // - `doo` for DeployOPChainOutput // - etc. From e8de7b191b0df2fd454fce6955bc5702e34bf297 Mon Sep 17 00:00:00 2001 From: Cypher Pepe <125112044+cypherpepe@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:29:12 +0300 Subject: [PATCH 210/451] chore: Fix Typos and Minor Edits in Documentation Files* (#12881) * typo corr 2022-02-02-inflation-vuln.md * typo corr README.md * typo corr RUNBOOK.md --- docs/postmortems/2022-02-02-inflation-vuln.md | 2 +- op-challenger/README.md | 2 +- op-conductor/RUNBOOK.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/postmortems/2022-02-02-inflation-vuln.md b/docs/postmortems/2022-02-02-inflation-vuln.md index a755b0fdfe38..a2a23e382303 100644 --- a/docs/postmortems/2022-02-02-inflation-vuln.md +++ b/docs/postmortems/2022-02-02-inflation-vuln.md @@ -58,7 +58,7 @@ timeline and activities were as follows: (Using github handles as identifiers) - 2022-02-02 1625: smartcontracts receives an e-mail from saurik claiming to have found a critical - issue in L2Geth. E-mail was sent to securityoptimism.io. + issue in L2Geth. E-mail was sent to security@optimism.io. - 2022-02-02 X: saurik messaged smartcontracts on Discord to make sure we checked the e-mail since he knew we had a prior problem where security advisories went to spam. - 2022-02-02 1650: Huddle begins in #security on Slack. diff --git a/op-challenger/README.md b/op-challenger/README.md index 97fafab8e6f7..5efbbd85b3d3 100644 --- a/op-challenger/README.md +++ b/op-challenger/README.md @@ -1,7 +1,7 @@ # op-challenger The `op-challenger` is a modular **op-stack** challenge agent written in -golang for dispute games including, but not limited to,attestation games, +golang for dispute games including, but not limited to, attestation games, fault games, and validity games. To learn more about dispute games, visit the [fault proof specs][proof-specs]. diff --git a/op-conductor/RUNBOOK.md b/op-conductor/RUNBOOK.md index 8e80fde6b6c0..00b8757a338e 100644 --- a/op-conductor/RUNBOOK.md +++ b/op-conductor/RUNBOOK.md @@ -28,7 +28,7 @@ OP_CONDUCTOR_NODE_RPC= # for example, http://op-node:8545 OP_CONDUCTOR_EXECUTION_RPC= # for example, http://op-geth:8545 OP_CONDUCTOR_NETWORK= # for example, base-mainnet, op-mainnet, etc, should be same as OP_NODE_NETWORK OP_CONDUCTOR_HEALTHCHECK_INTERVAL= # in seconds -OP_CONDUCTOR_HEALTHCHECK_UNSAFE_INTERVAL= # Interval allowed between unsafe head and now measured in seconds in seconds +OP_CONDUCTOR_HEALTHCHECK_UNSAFE_INTERVAL= # Interval allowed between unsafe head and now measured in seconds OP_CONDUCTOR_HEALTHCHECK_MIN_PEER_COUNT= # minimum number of peers required to be considered healthy OP_CONDUCTOR_RAFT_BOOTSTRAP=true/false # set to true if you want to bootstrap the raft cluster ``` From b3ec4d6972f1e1363a8fc5397b0ff434280be3a1 Mon Sep 17 00:00:00 2001 From: George Knee Date: Tue, 19 Nov 2024 10:34:16 +0000 Subject: [PATCH 211/451] batcher refactor: flatten out go routines (#12405) * flatten out batcher goroutines * move wg increment to parent fn * ensure mainloop closes before receipts loop * add comments * pass a context to both loops * remove debug lines * clean up mutex handling in receiptsLoop * don't need to set default value the first time * avoid writing to Logger while holding mutex * typo * increase log level and include tx.ID * fix changes from merge, hoist throttling loop goroutine launch to driver * call done on waitgroup in throttlingLoop * move function around hoping this makes the diff nicer --- op-batcher/batcher/driver.go | 78 ++++++++++++++++++++++-------------- op-batcher/readme.md | 2 +- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 2c9db3821e3a..7b1d6139e265 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -111,7 +111,7 @@ type BatchSubmitter struct { running bool txpoolMutex sync.Mutex // guards txpoolState and txpoolBlockedBlob - txpoolState int + txpoolState TxPoolState txpoolBlockedBlob bool // lastStoredBlock is the last block loaded into `state`. If it is empty it should be set to the l2 safe head. @@ -160,8 +160,20 @@ func (l *BatchSubmitter) StartBatchSubmitting() error { } } - l.wg.Add(1) - go l.loop() + receiptsCh := make(chan txmgr.TxReceipt[txRef]) + receiptsLoopCtx, cancelReceiptsLoopCtx := context.WithCancel(context.Background()) + throttlingLoopCtx, cancelThrottlingLoopCtx := context.WithCancel(context.Background()) + + // DA throttling loop should always be started except for testing (indicated by ThrottleInterval == 0) + if l.Config.ThrottleInterval > 0 { + l.wg.Add(1) + go l.throttlingLoop(throttlingLoopCtx) + } else { + l.Log.Warn("Throttling loop is DISABLED due to 0 throttle-interval. This should not be disabled in prod.") + } + l.wg.Add(2) + go l.processReceiptsLoop(receiptsLoopCtx, receiptsCh) // receives from receiptsCh + go l.mainLoop(l.shutdownCtx, receiptsCh, cancelReceiptsLoopCtx, cancelThrottlingLoopCtx) // sends on receiptsCh l.Log.Info("Batch Submitter started") return nil @@ -390,6 +402,8 @@ func (l *BatchSubmitter) calculateL2BlockRangeToStore(syncStatus eth.SyncStatus) // Submitted batch, but it is not valid // Missed L2 block somehow. +type TxPoolState int + const ( // Txpool states. Possible state transitions: // TxpoolGood -> TxpoolBlocked: @@ -399,13 +413,29 @@ const ( // send a cancellation transaction. // TxpoolCancelPending -> TxpoolGood: // happens once the cancel transaction completes, whether successfully or in error. - TxpoolGood int = iota + TxpoolGood TxPoolState = iota TxpoolBlocked TxpoolCancelPending ) -func (l *BatchSubmitter) loop() { +// setTxPoolState locks the mutex, sets the parameters to the supplied ones, and release the mutex. +func (l *BatchSubmitter) setTxPoolState(txPoolState TxPoolState, txPoolBlockedBlob bool) { + l.txpoolMutex.Lock() + l.txpoolState = txPoolState + l.txpoolBlockedBlob = txPoolBlockedBlob + l.txpoolMutex.Unlock() +} + +// mainLoop periodically: +// - polls the sequencer, +// - prunes the channel manager state (i.e. safe blocks) +// - loads unsafe blocks from the sequencer +// - drives the creation of channels and frames +// - sends transactions to the DA layer +func (l *BatchSubmitter) mainLoop(ctx context.Context, receiptsCh chan txmgr.TxReceipt[txRef], receiptsLoopCancel, throttlingLoopCancel context.CancelFunc) { defer l.wg.Done() + defer receiptsLoopCancel() + defer throttlingLoopCancel() queue := txmgr.NewQueue[txRef](l.killCtx, l.Txmgr, l.Config.MaxPendingTransactions) daGroup := &errgroup.Group{} @@ -419,22 +449,8 @@ func (l *BatchSubmitter) loop() { l.txpoolState = TxpoolGood l.txpoolMutex.Unlock() - // start the receipt/result processing loop - receiptsLoopDone := make(chan struct{}) - defer close(receiptsLoopDone) // shut down receipt loop l.l2BlockAdded = make(chan struct{}) defer close(l.l2BlockAdded) - receiptsCh := make(chan txmgr.TxReceipt[txRef]) - go l.processReceiptsLoop(receiptsCh, receiptsLoopDone) - - // DA throttling loop should always be started except for testing (indicated by ThrottleInterval == 0) - if l.Config.ThrottleInterval > 0 { - throttlingLoopDone := make(chan struct{}) - defer close(throttlingLoopDone) - go l.throttlingLoop(throttlingLoopDone) - } else { - l.Log.Warn("Throttling loop is DISABLED due to 0 throttle-interval. This should not be disabled in prod.") - } ticker := time.NewTicker(l.Config.PollInterval) defer ticker.Stop() @@ -467,34 +483,33 @@ func (l *BatchSubmitter) loop() { continue } l.publishStateToL1(queue, receiptsCh, daGroup, l.Config.PollInterval) - case <-l.shutdownCtx.Done(): + case <-ctx.Done(): l.Log.Warn("main loop returning") return } } } -func (l *BatchSubmitter) processReceiptsLoop(receiptsCh chan txmgr.TxReceipt[txRef], receiptsLoopDone chan struct{}) { +// processReceiptsLoop handles transaction receipts from the DA layer +func (l *BatchSubmitter) processReceiptsLoop(ctx context.Context, receiptsCh chan txmgr.TxReceipt[txRef]) { + defer l.wg.Done() l.Log.Info("Starting receipts processing loop") for { select { case r := <-receiptsCh: - l.txpoolMutex.Lock() if errors.Is(r.Err, txpool.ErrAlreadyReserved) && l.txpoolState == TxpoolGood { - l.txpoolState = TxpoolBlocked - l.txpoolBlockedBlob = r.ID.isBlob - l.Log.Info("incompatible tx in txpool", "is_blob", r.ID.isBlob) + l.setTxPoolState(TxpoolBlocked, r.ID.isBlob) + l.Log.Warn("incompatible tx in txpool", "id", r.ID, "is_blob", r.ID.isBlob) } else if r.ID.isCancel && l.txpoolState == TxpoolCancelPending { // Set state to TxpoolGood even if the cancellation transaction ended in error // since the stuck transaction could have cleared while we were waiting. - l.txpoolState = TxpoolGood + l.setTxPoolState(TxpoolGood, l.txpoolBlockedBlob) l.Log.Info("txpool may no longer be blocked", "err", r.Err) } - l.txpoolMutex.Unlock() l.Log.Info("Handling receipt", "id", r.ID) l.handleReceipt(r) - case <-receiptsLoopDone: - l.Log.Info("Receipts processing loop done") + case <-ctx.Done(): + l.Log.Info("Receipt processing loop done") return } } @@ -504,7 +519,8 @@ func (l *BatchSubmitter) processReceiptsLoop(receiptsCh chan txmgr.TxReceipt[txR // throttling of incoming data prevent the backlog from growing too large. By looping & calling the miner API setter // continuously, we ensure the engine currently in use is always going to be reset to the proper throttling settings // even in the event of sequencer failover. -func (l *BatchSubmitter) throttlingLoop(throttlingLoopDone chan struct{}) { +func (l *BatchSubmitter) throttlingLoop(ctx context.Context) { + defer l.wg.Done() l.Log.Info("Starting DA throttling loop") ticker := time.NewTicker(l.Config.ThrottleInterval) defer ticker.Stop() @@ -556,7 +572,7 @@ func (l *BatchSubmitter) throttlingLoop(throttlingLoopDone chan struct{}) { updateParams() case <-ticker.C: updateParams() - case <-throttlingLoopDone: + case <-ctx.Done(): l.Log.Info("DA throttling loop done") return } diff --git a/op-batcher/readme.md b/op-batcher/readme.md index 1d601fea94be..e09eb3fce2a9 100644 --- a/op-batcher/readme.md +++ b/op-batcher/readme.md @@ -50,7 +50,7 @@ When an L2 unsafe reorg is detected, the batch submitter will reset its state, a When a Tx fails, an asynchronous receipts handler is triggered. The channel from whence the Tx's frames came has its `frameCursor` rewound, so that all the frames can be resubmitted in order. ### Channel Times Out -When at Tx is confirmed, an asynchronous receipts handler is triggered. We only update the batcher's state if the channel timed out on chain. In that case, the `blockCursor` is rewound to the first block added to that channel, and the channel queue is cleared out. This allows the batcher to start fresh building a new channel starting from the same block -- it does not need to refetch blocks from the sequencer. +When a Tx is confirmed, an asynchronous receipts handler is triggered. We only update the batcher's state if the channel timed out on chain. In that case, the `blockCursor` is rewound to the first block added to that channel, and the channel queue is cleared out. This allows the batcher to start fresh building a new channel starting from the same block -- it does not need to refetch blocks from the sequencer. ## Design Principles and Optimization Targets At the current time, the batcher should be optimized for correctness, simplicity and robustness. It is considered preferable to prioritize these properties, even at the expense of other potentially desirable properties such as frugality. For example, it is preferable to have the batcher resubmit some data from time to time ("wasting" money on data availability costs) instead of avoiding that by e.g. adding some persistent state to the batcher. From aec36974369a238c112259b2f0131efb083ce68d Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Tue, 19 Nov 2024 03:19:26 -0800 Subject: [PATCH 212/451] document batcher throttling in op-batcher readme (#12957) --- op-batcher/readme.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/op-batcher/readme.md b/op-batcher/readme.md index e09eb3fce2a9..ba547845f099 100644 --- a/op-batcher/readme.md +++ b/op-batcher/readme.md @@ -60,6 +60,25 @@ The batcher can almost always recover from unforeseen situations by being restar Some complexity is permitted, however, for handling data availability switching, so that the batcher is not wasting money for longer periods of time. +### Data Availability Backlog + +A chain can potentially experience an influx of large transactions whose data availability requirements exceed the total +throughput of the data availability layer. While this situation might resolve on its own in the long term through the +data availability pricing mechanism, in practice this feedback loop is too slow to prevent a very large backlog of data +from being produced, even at a relatively low cost to whomever is submitting the large transactions. In such +circumstances, the safe head can fall significantly behind the unsafe head, and the time between seeing a transaction +(and charging it a given L1 data fee) and actually posting the transaction to the data availability layer grows larger +and larger. Because DA costs can rise quickly during such an event, the batcher can end up paying far more to post the +transaction to the DA layer than what can be recovered from the transaction's data fee. + +To prevent a significant DA backlog, the batcher can instruct the block builder (via op-geth's miner RPC API) to impose +thresholds on the total DA requirements of a single block, and/or the maximum DA requirement of any single +transaction. In the happy case, the batcher instructs the block builder to impose a block-level DA limit of +OP_BATCHER_THROTTLE_ALWAYS_BLOCK_SIZE, and imposes no additional limit on the DA requirements of a single +transaction. But in the case of a DA backlog (as defined by OP_BATCHER_THROTTLE_THRESHOLD), the batcher instructs the +block builder to instead impose a (tighter) block level limit of OP_BATCHER_THROTTLE_BLOCK_SIZE, and a single +transaction limit of OP_BATCHER_THROTTLE_TRANSACTION_SIZE. + ## Known issues and future work Link to [open issues with the `op-batcher` tag](https://github.com/ethereum-optimism/optimism/issues?q=is%3Aopen+is%3Aissue+label%3AA-op-batcher). From 8e0b89c116d0e294d9fb6e8c60a4bc70421b57d7 Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Tue, 19 Nov 2024 12:52:24 +0100 Subject: [PATCH 213/451] Update superchain-registry to include metal-sepolia Holocene activation (#12966) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 11a236812990..4fb2c9053db5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v1.0.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 - github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7 + github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6 github.com/ethereum/go-ethereum v1.14.11 github.com/fsnotify/fsnotify v1.8.0 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb @@ -250,7 +250,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.2-rc.1 +replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.2-rc.2 //replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/go.sum b/go.sum index 8436ca18ce06..7efcb5da40bd 100644 --- a/go.sum +++ b/go.sum @@ -187,10 +187,10 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.2-rc.1 h1:v314tR5EzG+QNE9aLf+goWCDsTT+RT2EsdOOlJT6CwM= -github.com/ethereum-optimism/op-geth v1.101411.2-rc.1/go.mod h1:RrPkuqfeIXkW28lQJwc5AG/BKbhkHRXPD5YezeeK4w8= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7 h1:Mbgsp5T52F2pEULHccLr4NtnT6cKnJgabpAPlTfPxrk= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241113154227-e72c6311f6e7/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= +github.com/ethereum-optimism/op-geth v1.101411.2-rc.2 h1:3suWTU9DwBdY8Yy/ZgZLB/yBy3TwpntpkUn61mZgNpY= +github.com/ethereum-optimism/op-geth v1.101411.2-rc.2/go.mod h1:dITJzx1KXsV2KusscsktidEb00blTSyFhalq8CjfsUY= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6 h1:+AIYWDX7FeWRLnBVqPiwireTacLLGGww1slGyv+YN0o= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= From 910c9ade39c0bcdff5f2badd94efbe016a428e73 Mon Sep 17 00:00:00 2001 From: George Knee Date: Tue, 19 Nov 2024 14:08:20 +0000 Subject: [PATCH 214/451] batcher: add batchSubmitter.checkExpectedProgress (#12430) * implement batchSubmitter.checkExpectedProgress * remove buffer variable * add warning logs when calling waitNodeSyncAndClearState * push method down into channel manager and add test * clarify SyncStatus documentation * improve TestChannelManager_CheckExpectedProgress make parameters "tighter" / more realistic and check an extra case --- op-batcher/batcher/channel_manager.go | 16 +++++++ op-batcher/batcher/channel_manager_test.go | 54 ++++++++++++++++++++++ op-batcher/batcher/driver.go | 30 ++++++++---- op-service/eth/sync_status.go | 2 +- 4 files changed, 93 insertions(+), 9 deletions(-) diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 4208a8cd3795..81ee0fb35a51 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -549,3 +549,19 @@ func (s *channelManager) PendingDABytes() int64 { } return int64(f) } + +// CheckExpectedProgress uses the supplied syncStatus to infer +// whether the node providing the status has made the expected +// safe head progress given fully submitted channels held in +// state. +func (m *channelManager) CheckExpectedProgress(syncStatus eth.SyncStatus) error { + for _, ch := range m.channelQueue { + if ch.isFullySubmitted() && // This implies a number of l1 confirmations has passed, depending on how the txmgr was configured + !ch.isTimedOut() && + syncStatus.CurrentL1.Number > ch.maxInclusionBlock && + syncStatus.SafeL2.Number < ch.LatestL2().Number { + return errors.New("safe head did not make expected progress") + } + } + return nil +} diff --git a/op-batcher/batcher/channel_manager_test.go b/op-batcher/batcher/channel_manager_test.go index 1c742207a5f0..32aae1b06dd1 100644 --- a/op-batcher/batcher/channel_manager_test.go +++ b/op-batcher/batcher/channel_manager_test.go @@ -627,3 +627,57 @@ func TestChannelManager_ChannelOutFactory(t *testing.T) { require.IsType(t, &ChannelOutWrapper{}, m.currentChannel.channelBuilder.co) } + +func TestChannelManager_CheckExpectedProgress(t *testing.T) { + l := testlog.Logger(t, log.LevelCrit) + cfg := channelManagerTestConfig(100, derive.SingularBatchType) + cfg.InitNoneCompressor() + m := NewChannelManager(l, metrics.NoopMetrics, cfg, defaultTestRollupConfig) + + channelMaxInclusionBlockNumber := uint64(3) + channelLatestSafeBlockNumber := uint64(11) + + // Prepare a (dummy) fully submitted channel + // with + // maxInclusionBlock and latest safe block number as above + A, err := newChannelWithChannelOut(l, metrics.NoopMetrics, cfg, m.rollupCfg, 0) + require.NoError(t, err) + rng := rand.New(rand.NewSource(123)) + a0 := derivetest.RandomL2BlockWithChainId(rng, 1, defaultTestRollupConfig.L2ChainID) + a0 = a0.WithSeal(&types.Header{Number: big.NewInt(int64(channelLatestSafeBlockNumber))}) + _, err = A.AddBlock(a0) + require.NoError(t, err) + A.maxInclusionBlock = channelMaxInclusionBlockNumber + A.Close() + A.channelBuilder.frames = nil + A.channelBuilder.frameCursor = 0 + require.True(t, A.isFullySubmitted()) + + m.channelQueue = append(m.channelQueue, A) + + // The current L1 number implies that + // channel A above should have been derived + // from, so we expect safe head to progress to + // the channelLatestSafeBlockNumber. + // Since the safe head moved to 11, there is no error: + ss := eth.SyncStatus{ + CurrentL1: eth.L1BlockRef{Number: channelMaxInclusionBlockNumber + 1}, + SafeL2: eth.L2BlockRef{Number: channelLatestSafeBlockNumber}, + } + err = m.CheckExpectedProgress(ss) + require.NoError(t, err) + + // If the currentL1 is as above but the + // safe head is less than channelLatestSafeBlockNumber, + // the method should return an error: + ss.SafeL2 = eth.L2BlockRef{Number: channelLatestSafeBlockNumber - 1} + err = m.CheckExpectedProgress(ss) + require.Error(t, err) + + // If the safe head is still less than channelLatestSafeBlockNumber + // but the currentL1 is _equal_ to the channelMaxInclusionBlockNumber + // there should be no error as that block is still being derived from: + ss.CurrentL1 = eth.L1BlockRef{Number: channelMaxInclusionBlockNumber} + err = m.CheckExpectedProgress(ss) + require.NoError(t, err) +} diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 7b1d6139e265..dde08ac84193 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -471,17 +471,20 @@ func (l *BatchSubmitter) mainLoop(ctx context.Context, receiptsCh chan txmgr.TxR l.state.pruneSafeBlocks(syncStatus.SafeL2) l.state.pruneChannels(syncStatus.SafeL2) + + err = l.state.CheckExpectedProgress(*syncStatus) + if err != nil { + l.Log.Warn("error checking expected progress, clearing state and waiting for node sync", "err", err) + l.waitNodeSyncAndClearState() + continue + } + if err := l.loadBlocksIntoState(*syncStatus, l.shutdownCtx); errors.Is(err, ErrReorg) { - // Wait for any in flight transactions - // to be ingested by the node before - // we start loading blocks again. - err := l.waitNodeSync() - if err != nil { - l.Log.Warn("error waiting for node sync", "err", err) - } - l.clearState(l.shutdownCtx) + l.Log.Warn("error loading blocks, clearing state and waiting for node sync", "err", err) + l.waitNodeSyncAndClearState() continue } + l.publishStateToL1(queue, receiptsCh, daGroup, l.Config.PollInterval) case <-ctx.Done(): l.Log.Warn("main loop returning") @@ -579,6 +582,17 @@ func (l *BatchSubmitter) throttlingLoop(ctx context.Context) { } } +func (l *BatchSubmitter) waitNodeSyncAndClearState() { + // Wait for any in flight transactions + // to be ingested by the node before + // we start loading blocks again. + err := l.waitNodeSync() + if err != nil { + l.Log.Warn("error waiting for node sync", "err", err) + } + l.clearState(l.shutdownCtx) +} + // waitNodeSync Check to see if there was a batcher tx sent recently that // still needs more block confirmations before being considered finalized func (l *BatchSubmitter) waitNodeSync() error { diff --git a/op-service/eth/sync_status.go b/op-service/eth/sync_status.go index f9db1f672b82..e16275920e2b 100644 --- a/op-service/eth/sync_status.go +++ b/op-service/eth/sync_status.go @@ -5,7 +5,7 @@ package eth type SyncStatus struct { // CurrentL1 is the L1 block that the derivation process is last idled at. // This may not be fully derived into L2 data yet. - // The safe L2 blocks were produced/included fully from the L1 chain up to and including this L1 block. + // The safe L2 blocks were produced/included fully from the L1 chain up to _but excluding_ this L1 block. // If the node is synced, this matches the HeadL1, minus the verifier confirmation distance. CurrentL1 L1BlockRef `json:"current_l1"` // CurrentL1Finalized is a legacy sync-status attribute. This is deprecated. From 50e1623a8d0945f00a79eb8a569884f2a59d7fb5 Mon Sep 17 00:00:00 2001 From: Axel Kingsley Date: Tue, 19 Nov 2024 09:12:12 -0600 Subject: [PATCH 215/451] interop: Reset Derivation and Backfill Supervisor when Too Far Behind (#12919) * Reset Derivation when Supervisor is Behind * Disable Batcher Throttling in Interop local-devnet --- interop-devnet/docker-compose.yml | 2 ++ op-node/rollup/interop/interop.go | 6 +++++- op-supervisor/supervisor/backend/db/fromda/update.go | 12 ++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/interop-devnet/docker-compose.yml b/interop-devnet/docker-compose.yml index de97c9967b64..c4cbab978a12 100644 --- a/interop-devnet/docker-compose.yml +++ b/interop-devnet/docker-compose.yml @@ -319,6 +319,7 @@ services: OP_BATCHER_METRICS_ENABLED: "true" OP_BATCHER_RPC_ENABLE_ADMIN: "true" OP_BATCHER_BATCH_TYPE: + OP_BATCHER_THROTTLE_INTERVAL: 0 # uncomment to use blobs # OP_BATCHER_DATA_AVAILABILITY_TYPE: blobs env_file: @@ -350,6 +351,7 @@ services: OP_BATCHER_METRICS_ENABLED: "true" OP_BATCHER_RPC_ENABLE_ADMIN: "true" OP_BATCHER_BATCH_TYPE: + OP_BATCHER_THROTTLE_INTERVAL: 0 # uncomment to use blobs # OP_BATCHER_DATA_AVAILABILITY_TYPE: blobs env_file: diff --git a/op-node/rollup/interop/interop.go b/op-node/rollup/interop/interop.go index a4342b6a19f6..94fa77a5b309 100644 --- a/op-node/rollup/interop/interop.go +++ b/op-node/rollup/interop/interop.go @@ -3,6 +3,7 @@ package interop import ( "context" "fmt" + "strings" "sync" "time" @@ -139,7 +140,10 @@ func (d *InteropDeriver) onInteropPendingSafeChangedEvent(x engine.InteropPendin defer cancel() if err := d.backend.UpdateLocalSafe(ctx, d.chainID, x.DerivedFrom, x.Ref.BlockRef()); err != nil { d.log.Debug("Failed to signal derived-from update to interop backend", "derivedFrom", x.DerivedFrom, "block", x.Ref) - // still continue to try and do a cross-safe update + if strings.Contains(err.Error(), "too far behind") { + d.log.Error("Supervisor is too far behind, resetting derivation", "err", err) + d.emitter.Emit(rollup.ResetEvent{Err: fmt.Errorf("supervisor is too far behind: %w", err)}) + } } // Now that the op-supervisor is aware of the new local-safe block, we want to check if cross-safe changed. d.emitter.Emit(engine.RequestCrossSafeEvent{}) diff --git a/op-supervisor/supervisor/backend/db/fromda/update.go b/op-supervisor/supervisor/backend/db/fromda/update.go index 146e558cf266..957df9e2dfa7 100644 --- a/op-supervisor/supervisor/backend/db/fromda/update.go +++ b/op-supervisor/supervisor/backend/db/fromda/update.go @@ -67,8 +67,10 @@ func (db *DB) AddDerived(derivedFrom eth.BlockRef, derived eth.BlockRef) error { derived, derived.ParentHash, lastDerived, types.ErrConflict) } } else if lastDerived.Number+1 < derived.Number { - return fmt.Errorf("derived block %s (parent: %s) is too new, expected to build on top of %s: %w", - derived, derived.ParentHash, lastDerived, types.ErrOutOfOrder) + return fmt.Errorf("cannot add block (%s derived from %s), last block (%s derived from %s) is too far behind: (%w)", + derived, derivedFrom, + lastDerived, lastDerivedFrom, + types.ErrOutOfOrder) } else { return fmt.Errorf("derived block %s is older than current derived block %s: %w", derived, lastDerived, types.ErrOutOfOrder) @@ -89,8 +91,10 @@ func (db *DB) AddDerived(derivedFrom eth.BlockRef, derived eth.BlockRef) error { } } else if lastDerivedFrom.Number+1 < derivedFrom.Number { // adding block that is derived from something too far into the future - return fmt.Errorf("cannot add block %s as derived from %s, still deriving from %s: %w", - derived, derivedFrom, lastDerivedFrom, types.ErrOutOfOrder) + return fmt.Errorf("cannot add block (%s derived from %s), last block (%s derived from %s) is too far behind: (%w)", + derived, derivedFrom, + lastDerived, lastDerivedFrom, + types.ErrOutOfOrder) } else { // adding block that is derived from something too old return fmt.Errorf("cannot add block %s as derived from %s, deriving already at %s: %w", From cd2df97287c07856e4249da94031ea040eaa1cde Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 19 Nov 2024 09:20:31 -0700 Subject: [PATCH 216/451] op-deployer: Test mainnet deployment (#12958) Adds a unit test for mainnet deployments at version 1.6.0. --- .circleci/config.yml | 1 + .../deployer/integration_test/apply_test.go | 46 +++++++++++------- .../testdata/allocs-l2-v160-1.json.gz | Bin 0 -> 152468 bytes .../testdata/allocs-l2-v160-11155111.json.gz | Bin 0 -> 152484 bytes .../testdata/allocs-l2-v160.json.gz | Bin 152466 -> 0 bytes 5 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-1.json.gz create mode 100644 op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-11155111.json.gz delete mode 100644 op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160.json.gz diff --git a/.circleci/config.yml b/.circleci/config.yml index 9fbed7bde44d..08a5d7afd3fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -867,6 +867,7 @@ jobs: export ENABLE_ANVIL=true export SEPOLIA_RPC_URL="https://ci-sepolia-l1-archive.optimism.io" + export MAINNET_RPC_URL="https://ci-mainnet-l1-archive.optimism.io" gotestsum --format=testname \ --junitfile=./tmp/test-results/results.xml \ diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 7f4c0cb06deb..0de23866946c 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -68,6 +68,8 @@ network_params: genesis_delay: 0 ` +const defaultL1ChainID uint64 = 77799777 + type deployerKey struct{} func (d *deployerKey) HDPath() string { @@ -99,7 +101,7 @@ func TestEndToEndApply(t *testing.T) { require.NoError(t, err) depKey := new(deployerKey) - l1ChainID := big.NewInt(77799777) + l1ChainID := new(big.Int).SetUint64(defaultL1ChainID) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) require.NoError(t, err) pk, err := dk.Secret(depKey) @@ -151,9 +153,19 @@ func TestEndToEndApply(t *testing.T) { } func TestApplyExistingOPCM(t *testing.T) { + t.Run("mainnet", func(t *testing.T) { + testApplyExistingOPCM(t, 1, os.Getenv("MAINNET_RPC_URL"), standard.L1VersionsMainnet) + }) + t.Run("sepolia", func(t *testing.T) { + testApplyExistingOPCM(t, 11155111, os.Getenv("SEPOLIA_RPC_URL"), standard.L1VersionsSepolia) + }) +} + +func testApplyExistingOPCM(t *testing.T, l1ChainID uint64, forkRPCUrl string, versions standard.L1Versions) { + op_e2e.InitParallel(t) + anvil.Test(t) - forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL") if forkRPCUrl == "" { t.Skip("no fork RPC URL provided") } @@ -177,20 +189,20 @@ func TestApplyExistingOPCM(t *testing.T) { l1Client, err := ethclient.Dial(runner.RPCUrl()) require.NoError(t, err) - l1ChainID := big.NewInt(11155111) + l1ChainIDBig := new(big.Int).SetUint64(l1ChainID) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) require.NoError(t, err) // index 0 from Anvil's test set pk, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") require.NoError(t, err) - l2ChainID := uint256.NewInt(1) + l2ChainID := uint256.NewInt(777) // Hardcode the below tags to ensure the test is validating the correct // version even if the underlying tag changes intent, st := newIntent( t, - l1ChainID, + l1ChainIDBig, dk, l2ChainID, artifacts.MustNewLocatorFromTag("op-contracts/v1.6.0"), @@ -214,7 +226,7 @@ func TestApplyExistingOPCM(t *testing.T) { validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) - releases := standard.L1VersionsSepolia.Releases["op-contracts/v1.6.0"] + releases := versions.Releases["op-contracts/v1.6.0"] implTests := []struct { name string @@ -287,7 +299,7 @@ func TestApplyExistingOPCM(t *testing.T) { require.EqualValues(t, expectedSemversL2, semvers) - f, err := os.Open("./testdata/allocs-l2-v160.json.gz") + f, err := os.Open(fmt.Sprintf("./testdata/allocs-l2-v160-%d.json.gz", l1ChainID)) require.NoError(t, err) defer f.Close() gzr, err := gzip.NewReader(f) @@ -370,7 +382,7 @@ func TestL2BlockTimeOverride(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - opts, intent, st := setupGenesisChain(t) + opts, intent, st := setupGenesisChain(t, defaultL1ChainID) intent.GlobalDeployOverrides = map[string]interface{}{ "l2BlockTime": float64(3), } @@ -388,7 +400,7 @@ func TestApplyGenesisStrategy(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - opts, intent, st := setupGenesisChain(t) + opts, intent, st := setupGenesisChain(t, defaultL1ChainID) require.NoError(t, deployer.ApplyPipeline(ctx, opts)) @@ -408,7 +420,7 @@ func TestProofParamOverrides(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - opts, intent, st := setupGenesisChain(t) + opts, intent, st := setupGenesisChain(t, defaultL1ChainID) intent.GlobalDeployOverrides = map[string]any{ "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, "minProposalSizeBytes": standard.MinProposalSizeBytes + 1, @@ -505,7 +517,7 @@ func TestInteropDeployment(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - opts, intent, st := setupGenesisChain(t) + opts, intent, st := setupGenesisChain(t, defaultL1ChainID) intent.UseInterop = true require.NoError(t, deployer.ApplyPipeline(ctx, opts)) @@ -523,7 +535,7 @@ func TestAltDADeployment(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - opts, intent, st := setupGenesisChain(t) + opts, intent, st := setupGenesisChain(t, defaultL1ChainID) altDACfg := genesis.AltDADeployConfig{ UseAltDA: true, DACommitmentType: altda.KeccakCommitmentString, @@ -601,7 +613,7 @@ func TestInvalidL2Genesis(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - opts, intent, _ := setupGenesisChain(t) + opts, intent, _ := setupGenesisChain(t, defaultL1ChainID) intent.DeploymentStrategy = state.DeploymentStrategyGenesis intent.GlobalDeployOverrides = tt.overrides @@ -612,11 +624,11 @@ func TestInvalidL2Genesis(t *testing.T) { } } -func setupGenesisChain(t *testing.T) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) { +func setupGenesisChain(t *testing.T, l1ChainID uint64) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) { lgr := testlog.Logger(t, slog.LevelDebug) depKey := new(deployerKey) - l1ChainID := big.NewInt(77799777) + l1ChainIDBig := new(big.Int).SetUint64(l1ChainID) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) require.NoError(t, err) @@ -627,8 +639,8 @@ func setupGenesisChain(t *testing.T) (deployer.ApplyPipelineOpts, *state.Intent, loc, _ := testutil.LocalArtifacts(t) - intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) - intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) + intent, st := newIntent(t, l1ChainIDBig, dk, l2ChainID1, loc, loc) + intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainIDBig, l2ChainID1)) intent.DeploymentStrategy = state.DeploymentStrategyGenesis opts := deployer.ApplyPipelineOpts{ diff --git a/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-1.json.gz b/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..7a5450d641911eee9b419f451135296b9a73585f GIT binary patch literal 152468 zcmb4}cTiK?7w^65wJU-JL5k9mCSCefkPZ=$ULqxgju3hXTnk7qp@tTvx6q4}Br3fV zT7W>18hV5P0Yc#M-v946bLTsAzH?^n X&)H}1HHS0$+BKRf!%5 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%0AP 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

c%mls9%ROMYa0&t@mNwTbPGb` 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 WzuK+;{*V9ukN+PDe`r`$1&{#wx+CQP literal 0 HcmV?d00001 diff --git a/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-11155111.json.gz b/op-deployer/pkg/deployer/integration_test/testdata/allocs-l2-v160-11155111.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..2fed12ccec471ede0edd95f3b2d92983e88d0477 GIT binary patch literal 152484 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

<^N6l{}H9ZnTM{-iYVfD=5g0C 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 *bIZ28HPur$E^M!jW?g@ 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